суббота, 27 августа 2011 г.

Колонка в строку. WM_CONCAT и LISTAGG

Агрегирование группы записей в одну строку.

Хотя такая группировка противоречит правилу первой нормальной формы (атомарность), в ORACLE предусмотрена функция для подобного преобразования.
Для примера, есть таблица CALENDAR, которая содержит номера кварталов и названия месяцев.
select distinct C.QUARTER_ID, C.MONTH_NAME_EN, C.MONTH_ID from calendar c
QUARTER_IDMONTH_NAME_ENMONTH_ID
01january01
01february02
01march03
02april04
02may05
02june06
03july07
03august08
03september09
04october10
04november11
04december12


Нужно записать месяцы каждого квартала в одну строку.

Функция WM_CONCAT агрегирует записи в одну строку. Функция принадлежит пользователю WMSYS. В зависимости от конфигурации базы этого пользователя (а значит и функции) может и не быть. Второй недостаток - в качестве разделителя по умолчанию значится запятая, но с этим можно успешно бороться посредством REPLACE. В приведенном примере с помощью REPLACE к запятой добавлен пробел для придания тексту более читабельного вида.
select C.QUARTER_ID, replace(wm_concat(C.MONTH_NAME_EN), ',', ', ') as months  
from
(select distinct C.QUARTER_ID QUARTER_ID, C.MONTH_NAME_EN MONTH_NAME_EN, C.MONTH_ID MONTH_ID
from calendar c) c
group by C.QUARTER_ID
order by 1
Как видно из результата в WM_CONCAT сортировка также не предусмотрена :
QUARTER_IDMONTHS
01january, february, march
02june, may, april
03august, september, july
04november, december, october


В ORACLE 11g Release 2 появилась функция LISTAGG, которая позволяет задавать разделитель, производить сортировку внутри строки.
select C.QUARTER_ID, 
LISTAGG(C.MONTH_NAME_EN, ', ') WITHIN GROUP (ORDER BY C.MONTH_ID) AS MONTHS 
from 
(select distinct C.QUARTER_ID QUARTER_ID, C.MONTH_NAME_EN MONTH_NAME_EN, C.MONTH_ID MONTH_ID
from calendar c) c
group by C.QUARTER_ID
order by 1
Результат:
QUARTER_IDMONTHS
01january, february, march
02april, may, june
03july, august, september
04october, november, december


Функция LISTAGG возвращает данные в формате varchar что накладывает соответствующие ограничения на результат.