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

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

пример:

Таблица A

PID
A
B
C

Таблица B

PID   SEQ    Desc

A     1      Have
A     2      a nice
A     3      day.
B     1      Nice Work.
C     1      Yes
C     2      we can 
C     3      do 
C     4      this work!

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

PID   Desc
A     Have a nice day.
B     Nice Work.
C     Yes we can do this work!

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

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

13 ответов


есть несколько способов, в зависимости от того, какая версия у вас - вижу документация oracle по методам агрегирования строк. Очень распространенным является использование LISTAGG:

SELECT pid, LISTAGG(Desc, ' ') WITHIN GROUP (ORDER BY seq) AS description
FROM B GROUP BY pid;
вступить A, чтобы выбрать pids вы хотите.

Примечание: из коробки, LISTAGG корректно работает только с VARCHAR2 столбцы.


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

С XMLAGG вы можете сделать следующее:

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

это

  • поставить значения ename столбец (объединенный запятой) из employee_names таблица в элементе xml (с тегом E)
  • извлечь текст этой
  • агрегировать xml (объединить его)
  • вызовите результирующий столбец "Result"

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


на LISTAGG аналитическая функция была введена в Oracle 11g Release 2, что делает его очень легко агрегировать строки. Если вы используете 11g Release 2, вы должны использовать эту функцию для агрегации строк. См. ниже url-адрес для получения дополнительной информации о конкатенации строк.

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

Конкатенация Строк


как и большинство ответов,LISTAGG является очевидным вариантом. Однако, один раздражающий аспект с LISTAGG это если общая длина сцепленной строки превышает 4000 символов( ограничение для VARCHAR2 В SQL), ниже выбрасывается ошибка, которой трудно управлять в версиях 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..' at конец строки (по умолчанию ... )
  • ON OVERFLOW TRUNCATE '': это отобразит 4000 символов без какой-либо завершающей строки.
  • ON OVERFLOW TRUNCATE WITH COUNT : это будет отображаться общее количество символов в конце после окончания символов. Например:- '...(5512)'
  • ON OVERFLOW ERROR : если вы ожидаете LISTAGG завершится ORA-01489 ошибка (которая по умолчанию в любом случае ).

для тех, кто должен решить эту проблему с помощью 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, а список PIDs может быть переупорядочен в предложении final 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


перед запуском запроса select выполните следующее:

SET SERVEROUT ON SIZE 6000

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

Я использую LISTAGG, но возвращаю эту строку для персидской строки !

мой запрос:

SELECT
 listagg(DESCRIPTION,' , ') within group (order by DESCRIPTION) 
FROM
B_CEREMONY

результат:

'A7'1 , ,4F

пожалуйста, помогите мне.

wow это решение работает:

SELECT listagg(convert(DESCRIPTION, 'UTF8', 'AL16UTF16'),' , ') within group 
(order by DESCRIPTION) 
FROM  B_CEREMONY;

11G и выше: использовать listagg:

SELECT 
    col1,
    LISTAGG(col2, ', ') WITHIN GROUP (ORDER BY col2) "names"
FROM table_x
GROUP BY col1

10G и ниже: один из методов заключается в использовании функции:

CREATE OR REPLACE FUNCTION get_comma_separated_value (input_val  in  number)
  RETURN VARCHAR2
IS
  return_text  VARCHAR2(10000) := NULL;
BEGIN
  FOR x IN (SELECT col2 FROM table_name WHERE col1 = input_val) LOOP
    return_text := return_text || ',' || x.col2 ;
  END LOOP;
  RETURN LTRIM(return_text, ',');
END;
/

чтобы использовать функцию:

select col1, get_comma_separated_value(col1) from table_name

Примечание: есть функция (не поддерживается)WM_CONCAT доступно в некоторых старых версиях Oracle, которые могут помочь вам - см. подробнее здесь.

В MySQL:

SELECT col1, GROUP_CONCAT(col2) FROM table_name GROUP BY col1

  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. сбор с заказом немного медленнее(00:00:07.08):

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

все остальные методы были медленнее.


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

 SELECT XMLAGG(XMLELEMENT(E,fieldname||',')).EXTRACT('//text()') "FieldNames"
    FROM FIELD_MASTER
    WHERE FIELD_ID > 10 AND FIELD_AREA != 'NEBRASKA';

или функция Oracle STRAGG(столбец).

Я должен сказать, что этот вид обработки очень ограничен ... если вы превышаете ширину поля или ширину отображения ...


в поле выбрать, где требуется конкатенация, вызовите функцию 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

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