Как функцией получить отсортированную строку значений с разделителем без повторений?
Как написать PL/SQL функцию, которая на вход принимает строку значений с разделителем по-умолчанию «;», а как результат возвращает строку в этом же формате, но со значениями без повторений и отсортированными в алфавитном порядке?
Например, результатом обработки строки:
test12 34;test 12;abv;abvavb;test 12;
будет такая строка:
abv;abvavb;test 12;test12 34;
Ответы (2 шт):
Самый простой способ (на db<>fiddle):
create function normalize (str varchar2, delimiter char := ';') return varchar2 is
ret varchar2 (32000);
begin
select listagg (col, ';') within group (order by col)||delimiter into ret
from (
select distinct trim (column_value) col
from xmlTable (('"'||replace (str, delimiter, '","')||'"')));
return ret;
end normalize;
/
Так запустить и получить результат:
var result varchar2
exec :result := normalize (str=>'test12 34;test 12;abv;abvavb;test 12;')
RESULT
-----------------------------
abv;abvavb;test 12;test12 34;
Реализация функции на чистом PL/SQL с ассоциативной коллекцией для сохранения промежуточного результата. Причём, сохранять надо только ключи, т.к ключи ассоциативной коллекции по своей проироде - уникальны и отсортированы. Так как, нет переключения контекста выполнения, это будет наиболее эффективным по производительности решением.
create or replace function normalize (str varchar2, delimiter char := ';') return varchar2 is
type aarr is table of char (1) index by varchar2 (96);
function split (str varchar2, delimiter char) return aarr is
type posvals is record (s int := 1, e int := 0);
pos posvals;
tokens aarr;
len constant int := length (str);
begin
<<split>> loop
pos.e := instr (str, delimiter, pos.s);
exit split when pos.e = 0 and pos.s >= len;
if pos.e = 0 then pos.e := len+1; end if;
tokens(substr (str, pos.s, pos.e-pos.s)) := null;
pos.s := pos.e + length (delimiter);
end loop;
return tokens;
end split;
function join (aa aarr, delimiter char) return varchar2 is
key varchar2 (96) := aa.first;
ret varchar2 (32000);
begin
while key is not null loop
ret := ret||key||delimiter;
key := aa.next (key);
end loop;
return ret;
end join;
begin
return (join (split (str, delimiter), delimiter));
end normalize;
/
Запуск и результат (на db<>fiddle):
var result varchar2
exec :result := normalize (str=>'test12 34;test 12;abv;abvavb;test 12;')
RESULT
-----------------------------
abv;abvavb;test 12;test12 34;