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


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

пример:

Стол

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 116

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 (объединить его)
  • вызовите результирующий столбец "результат"

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

The 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 может быть переупорядочен в конечном предложении 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;

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

несколько других ответов на этой странице также упоминали эту чрезвычайно полезную ссылку: 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

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

Вау это решение сработало:

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. сбор обеспечивает лучшую производительность, если сортировка не требуется(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

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