SQL Запрос к значениям столбцов из нескольких строк в Oracle

StackOverflow https://stackoverflow.com/questions/4686543

  •  11-10-2019
  •  | 
  •  

Вопрос

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

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

Таблица а

PID
A
B
C

Таблица б

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 Out Table - это конкатенация значений SEQ из таблицы B?

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

Это было полезно?

Решение

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

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

Для тех, кто должен решить эту проблему, используя Oracle 9i (или более раннее), вам, вероятно, потребуется использовать sys_connect_by_path, так как Listaggag не доступен.

Чтобы ответить на 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;

Все значения могут быть переупорядочены по желанию. Индивидуальные объединенные описания могут быть переупорядочены в разделе «Разделение по поводу», а список PID может быть переупорядочен в пункте «Окончательный заказ по пункту».


Альтернативно: Могут быть времена, когда вы хотите объединить все значения из всей таблицы в одну строку.

Ключевая идея здесь заключается в использовании искусственного значения для группы описаний.

В следующем запросе используется постоянная строка '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-aggreation-techniques

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

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;

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

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

В выборе, где вы хотите, чтобы ваша конкатенация вызовите функцию 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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top