Есть ли в PL/SQL эквивалент T-SQL сет ориентированной конкатенации переменной?
В Microsoft SQL Server (T-SQL) существует лаконичный, элегантный и высокопроизводительный способ заполнения переменной с типом VARCHAR конкатенцией значений одной колонки из нескольких строк в таблице, например:
DECLARE @vals NVARCHAR(MAX)
SELECT @vals = ISNULL(@vals + ',', '')
+ <some_varchar_column>
FROM <some_table>
Переменная @vals заполнится строкой с запятой как разделитель из всех значений в some_column всех записей some_table, например: value1,value2,value3, ....
Как сделать что-то подобное в PL/SQL таким же элегантным способом (без необходимости писать цикл или курсор)?
Свободный перевод вопроса PL/SQL equivalent of T-SQL set-oriented variable concatenation от участника Cade Bryant
Ответы (2 шт):
Вот так:
select listagg(dname, ',') within group (order by dname) result
from dept;
RESULT
-------------------------------------------------------------------------
ACCOUNTING,OPERATIONS,RESEARCH,SALES
На dbfiddle с тестовой таблицей.
Свободный перевод ответа от участника @Littlefoot
Нет, прямого эквивалента в PL/SQL нет. Есть SQL функция LISTAGG, которая возвращает аналогичный результат.
Но иногда всё же имеет смысл сделать свою собственную функцию. Это довольно просто:
select * from vals
/
ID VAL
---------- ---------------------------------------------
1 value1
1 value2
2 value3
2 value4
2 value5
3 value6
create or replace type listofvals is table of varchar2 (64)
/
with function join (vals listofvals, delimiter char := ',') return varchar2 is
ret varchar2 (32767);
begin
for i in 1..vals.count loop
ret := ret||vals(i)||delimiter; end loop;
return rtrim (ret, delimiter);
end;
select id, join (cast (collect (val order by val) as listofvals), ' -> ') res
from vals
group by id
/
Результат:
ID RES
---------- --------------------------------
1 value1 -> value2
2 value3 -> value4 -> value5
3 value6
PS В версии 20c ожидается новый тип - итератор, который позволит записать for-loop более лаконично.