blog.mgsxx.com

Програмиране

Не е толкова често, но редовно трябва да използвам SQL, за да обединя всички редове в селекция някъде по следния начин:

Проблемът е, че нито стандартният SQL, нито Oracle (доскоро) нямаха функция като CONCAT. Какво може да се направи?

Потребителска функция

Можете да напишете своя собствена PL/SQL обобщена функция. Не че изобщо не беше забавно, но кодът трябва да бъде надлежно надраскан.

Основният проблем при този подход е SQL обвързването с вашата база данни. Ако дадете своя SQL на някого, трябва да му дадете и функцията.

Оказва се, че Oracle има функция, наречена WM_CONCAT, която прави точно това, което трябва да направи:

Той дори поддържа DISTINCT. Това е добре, но тази функция не се поддържа официално от Oracle и СТРАШНИ ГРЪБОВЕ. Той може безшумно да върне NULL. Може би изведнъж (точно какво изведнъж, всичко е работило вчера, но днес планът се е променил) започне да дава грешки, например ORA-22922. Така че не използвайте тази функция!

Oracle 11g Release 2 най-накрая добави стандартна вградена агрегирана функция за обединяване: LISTAGG. Това е добре за всички: изрично е посочен редът на сортиране на стойностите, може да се посочи произволен разделител, работи с DISTINCT, може да се използва като аналитична функция.

Функцията има само два недостатъка. Първо, няма начин да се върне CLOB, а не VARCHAR2; следователно дължината на резултата е ограничена. WM_CONCAT, например, връща CLOB. Но този недостатък може дори да е предимство, тъй като такива дълги редове обикновено не са необходими и е грозно да се правят касти за пореден път. На второ място ... е, това е временен недостатък, но все пак уместен: не всички са преминали към най-новия Oracle.

LISTAGG има още един, редовно много досаден недостатък: DISTINCT не се поддържа. Това, което попречи на фиксирането на функцията, е загадка извън моето разбиране. Тъй като сортирането на стойностите е извършено, какви технически проблеми има при премахването на дубликати по пътя?

Какво да направите, трябва да се отървете от него. Една от опциите е опцията за подзаявка, описана по-долу, в която DISTINCT вече е направена. Досадно и не винаги възможно. Хората измислиха друг начин: премахване на дубликати с регулярни изрази. Ето пример от моята практика: