SQL-запит для об'єднання значень стовпців з декількох рядків в Oracle


169

Чи можна було б побудувати SQL для об'єднання значень стовпців з декількох рядків?

Наступний приклад:

Таблиця А

ПІД
А
Б
С

Таблиця В

Опис PID SEQ

А 1 маємо
A 2 приємно
3 день.
B 1 Приємна робота.
C 1 Так
C 2 ми можемо 
C 3 робити 
C 4 ця робота!

Вихід SQL повинен бути -

Опис PID
Приємного дня.
B Приємна робота.
C Так, ми можемо зробити цю роботу!

Отже, в основному стовпець Desc для викладеної таблиці є об'єднанням значень SEQ з таблиці B?

Будь-яка допомога з SQL?


Дивіться наприклад: halisway.blogspot.com/2006/08/…
Andomar

Будь ласка, подивіться на це рішення . Це вам стане в нагоді.
Jineesh Uvantavida

Відповіді:


237

Існує кілька способів залежно від версії, яку ви маєте - дивіться документацію на oracle про методи агрегації рядків . Дуже поширеним є використання LISTAGG:

SELECT pid, LISTAGG(Desc, ' ') WITHIN GROUP (ORDER BY seq) AS description
FROM B GROUP BY pid;

Тоді приєднуйтесь, щоб Aвибрати бажане pids.

Примітка: поза полем, LISTAGGправильно працює тільки зі VARCHAR2стовпцями.


2
використовуючи wm_concat () для Oracle 10g, об'єднує текст у порядку зростання, порядковий номер, розміщений комами, чи можемо ми зробити спадне обмеження чимось іншим?
jagamot

19

Є також XMLAGGфункція, яка працює на версіях до 11.2. Оскільки OracleWM_CONCAT є недокументованим і не підтримується , рекомендується не використовувати його у виробничій системі.

З XMLAGGвами можна зробити наступне:

SELECT XMLAGG(XMLELEMENT(E,ename||',')).EXTRACT('//text()') "Result" 
FROM employee_names

Що це робить

  • помістіть значення enameстовпця (об'єднаного комою) з employee_namesтаблиці в елемент xml (з тегом E)
  • витягнути текст цього
  • агрегувати xml (об'єднати його)
  • викликати отриманий стовпець "Результат"

XMLAGG працює над Oracle 12.2. Більше того, XLMAGG дозволяє увімкнути дуже довгі рядки, які LISTAGG можуть не мати через остаточну довжину.
Марко

13

З пропозицією про модель SQL:

SQL> select pid
  2       , ltrim(sentence) sentence
  3    from ( select pid
  4                , seq
  5                , sentence
  6             from b
  7            model
  8                  partition by (pid)
  9                  dimension by (seq)
 10                  measures (descr,cast(null as varchar2(100)) as sentence)
 11                  ( sentence[any] order by seq desc
 12                    = descr[cv()] || ' ' || sentence[cv()+1]
 13                  )
 14         )
 15   where seq = 1
 16  /

P SENTENCE
- ---------------------------------------------------------------------------
A Have a nice day
B Nice Work.
C Yes we can do this work!

3 rows selected.

Про це я писав тут . І якщо ви перейдете за посиланням на потік OTN, ви знайдете ще декілька, включаючи порівняння продуктивності.


10

LISTAGG аналітична функція була введена в Oracle 11g Release 2 , що робить його дуже легко агрегатні рядки. Якщо ви використовуєте 11g Release 2, ви повинні використовувати цю функцію для агрегації рядків. Будь ласка, зверніться до URL-адреси для отримання додаткової інформації про з'єднання рядків.

http://www.oracle-base.com/articles/misc/StringAggregationTechniques.php

Об'єднання рядків


8

Як свідчить більшість відповідей, LISTAGGочевидний варіант. Однак один з прикріх аспектів LISTAGGполягає в тому, що якщо загальна довжина об'єднаного рядка перевищує 4000 символів (обмеження для VARCHAR2в SQL), подається нижча помилка, якою важко керувати у версіях Oracle upto 12.1

ORA-01489: результат конкатенації рядків занадто довгий

Нова функція, додана в 12cR2, є ON OVERFLOWпунктом LISTAGG. Запит, що включає цей пункт, виглядатиме так:

SELECT pid, LISTAGG(Desc, ' ' on overflow truncate) WITHIN GROUP (ORDER BY seq) AS desc
FROM B GROUP BY pid;

Вищенаведене обмежить вихід на 4000 символів, але не призведе до ORA-01489помилки.

Ось деякі додаткові параметри ON OVERFLOWпункту:

  • ON OVERFLOW TRUNCATE 'Contd..' : Це відобразиться 'Contd..'в кінці рядка (за замовчуванням ...)
  • ON OVERFLOW TRUNCATE '' : На цьому екрані буде показано 4000 символів без будь-якого завершального рядка.
  • ON OVERFLOW TRUNCATE WITH COUNT: На екрані відобразиться загальна кількість символів після закінчення символів. Напр .: - ' ...(5512)'
  • ON OVERFLOW ERROR: Якщо ви очікуєте, що помилка LISTAGGвийде з ладу ORA-01489(яка все одно за замовчуванням).

6

Для тих, хто повинен вирішити цю проблему за допомогою Oracle 9i (або раніше), вам, ймовірно, потрібно буде використовувати SYS_CONNECT_BY_PATH, оскільки LISTAGG недоступний.

Щоб відповісти на ОП, наступний запит відобразить PID з таблиці A і об'єднає всі стовпці DESC з таблиці B:

SELECT pid, SUBSTR (MAX (SYS_CONNECT_BY_PATH (description, ', ')), 3) all_descriptions
FROM (
       SELECT ROW_NUMBER () OVER (PARTITION BY pid ORDER BY pid, seq) rnum, pid, description
       FROM (
              SELECT a.pid, seq, description
              FROM table_a a, table_b b
              WHERE a.pid = b.pid(+)
             )
      )
START WITH rnum = 1
CONNECT BY PRIOR rnum = rnum - 1 AND PRIOR pid = pid
GROUP BY pid
ORDER BY pid;

Також можуть бути випадки, коли ключі та значення містяться в одній таблиці. Наступний запит можна використовувати там, де немає Таблиці А, і існує лише Таблиця B:

SELECT pid, SUBSTR (MAX (SYS_CONNECT_BY_PATH (description, ', ')), 3) all_descriptions
FROM (
       SELECT ROW_NUMBER () OVER (PARTITION BY pid ORDER BY pid, seq) rnum, pid, description
       FROM (
              SELECT pid, seq, description
              FROM table_b
             )
      )
START WITH rnum = 1
CONNECT BY PRIOR rnum = rnum - 1 AND PRIOR pid = pid
GROUP BY pid
ORDER BY pid;

Усі значення можна переупорядкувати за бажанням. Окремі об'єднані описи можуть бути переупорядковані в пункті PARTITION BY, а список PID можна переупорядкувати в заключному пункті ORDER BY.


По черзі: можуть бути випадки, коли ви хочете об'єднати всі значення з усієї таблиці в один ряд.

Ключова ідея тут полягає у використанні штучного значення для групи описів, які слід об'єднати.

У наступному запиті використовується постійний рядок '1', але будь-яке значення буде працювати:

SELECT SUBSTR (MAX (SYS_CONNECT_BY_PATH (description, ', ')), 3) all_descriptions
FROM (
       SELECT ROW_NUMBER () OVER (PARTITION BY unique_id ORDER BY pid, seq) rnum, description
       FROM (
              SELECT '1' unique_id, b.pid, b.seq, b.description
              FROM table_b b
             )
      )
START WITH rnum = 1
CONNECT BY PRIOR rnum = rnum - 1;

Окремі об'єднані описи можуть бути переупорядковані у пункті PARTITION BY.

Кілька інших відповідей на цій сторінці також згадували цю надзвичайно корисну довідку: https://oracle-base.com/articles/misc/string-aggregation-techniques


3
  1. LISTAGG забезпечує найкращі показники, якщо сортування є обов'язковим (00: 00: 05.85)

    SELECT pid, LISTAGG(Desc, ' ') WITHIN GROUP (ORDER BY seq) AS description FROM B GROUP BY pid;

  2. COLLECT забезпечує найкращі показники, якщо сортування не потрібне (00: 00: 02.90):

    SELECT pid, TO_STRING(CAST(COLLECT(Desc) AS varchar2_ntt)) AS Vals FROM B GROUP BY pid;

  3. COLLECT із замовленням трохи повільніше (00: 00: 07.08):

    SELECT pid, TO_STRING(CAST(COLLECT(Desc ORDER BY Desc) AS varchar2_ntt)) AS Vals FROM B GROUP BY pid;

Усі інші методи були повільнішими.


1
Було б корисно детальніше розглянути вашу відповідь.
Джон Суррелл

Джон, я не хотів повторювати статтю, але коротко це такі результати: 1. LISTAGG забезпечує найкращі показники, якщо сортування є обов'язковим (00: 00: 05.85) 2. COLLECT забезпечує найкращі показники, якщо сортування не є потрібно (00: 00: 02.90): ВИБІРТИ Pid, ​​TO_STRING (CAST (COLLECT (Desc) AS varchar2_ntt)) AS AS Vals ОТ B GROUP BY pid; 3. COLLECT з замовленням трохи повільніше (00: 00: 07.08): ВИБІРТЕ Pid, ​​TO_STRING (CAST (COLLECT (Опис замовлення на Desc) AS varchar2_ntt)) AS Vals ОТ B GROUP BY pid; Усі інші методи були повільнішими.
Мішо

1
Ви можете просто відредагувати свою відповідь, щоб включити відповідну інформацію.
Джон Суррелл

Я запізнився в редагуванні, і тому я знову додав його. Вибачте, що я новачок тут і тільки починаю розвішувати це.
Мішо

1

Перш ніж запустити обраний запит, запустіть цей:

SET SERVEROUT ON SIZE 6000

SELECT XMLAGG(XMLELEMENT(E,SUPLR_SUPLR_ID||',')).EXTRACT('//text()') "SUPPLIER" 
FROM SUPPLIERS;


-3

У виборі місця, де ви хочете конкатенацію, викликайте функцію SQL.

Наприклад:

select PID, dbo.MyConcat(PID)
   from TableA;

Потім для функції SQL:

Function MyConcat(@PID varchar(10))
returns varchar(1000)
as
begin

declare @x varchar(1000);

select @x = isnull(@x +',', @x, @x +',') + Desc
  from TableB
    where PID = @PID;

return @x;

end

Синтаксис заголовка функції може бути неправильним, але принцип працює.


Це недійсне для Oracle
a_horse_with_no_name
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.