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

169

Можно ли построить SQL для объединения значений столбцов из нескольких строк?

Ниже приведен пример:

Таблица А

PID

В
С

Таблица Б

PID SEQ Desc

1 есть
А 2 хороший
3 дня.
B 1 Хорошая работа.
C 1 Да
С 2 мы можем 
С 3 до 
С 4 это работа!

Вывод SQL должен быть -

PID Desc
Хорошего дня.
Хорошая работа.
C Да, мы можем сделать эту работу!

Таким образом, в основном столбец Desc для выходной таблицы представляет собой объединение значений SEQ из таблицы B?

Любая помощь с SQL?

jagamot
источник
См. Например: halisway.blogspot.com/2006/08/…
Andomar
Пожалуйста, посмотрите на это решение . Это будет полезно для вас.
Джиниш Увантавида

Ответы:

237

Есть несколько способов, в зависимости от того, какая у вас версия - смотрите документацию оракула по методам агрегирования строк . Очень распространенным является использование 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 объединяет текст в порядке возрастания порядкового номера, разделенного запятыми, можем ли мы сделать нисходящий, разделенный чем-то другим?
Джагамот
19

Также есть XMLAGGфункция, которая работает на версиях до 11.2. Так WM_CONCATкак Oracle не документирован и не поддерживается , рекомендуется не использовать его в производственной системе.

С помощью 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 символов (ограничение для VARCHAR2SQL), выдается следующая ошибка, которой трудно управлять в версиях Oracle до 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 недоступен.

Чтобы ответить на OP, следующий запрос отобразит 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;

Также могут быть случаи, когда все ключи и значения содержатся в одной таблице. Следующий запрос можно использовать, если нет таблицы A и существует только таблица 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

JonathanDavidArndt
источник
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): 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

Перед тем, как запустить запрос на выбор, запустите:

SET SERVEROUT ON SIZE 6000

SELECT XMLAGG(XMLELEMENT(E,SUPLR_SUPLR_ID||',')).EXTRACT('//text()') "SUPPLIER" 
FROM SUPPLIERS;
user2865810
источник
-1

Попробуйте этот код:

 SELECT XMLAGG(XMLELEMENT(E,fieldname||',')).EXTRACT('//text()') "FieldNames"
    FROM FIELD_MASTER
    WHERE FIELD_ID > 10 AND FIELD_AREA != 'NEBRASKA';
Кришнакумар MD Amain Infotech
источник
-3

В select, где вы хотите свою конкатенацию, вызовите функцию 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

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

user5473005
источник
Это
неверно