SQL Запрос к значениям столбцов из нескольких строк в Oracle
-
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
Листагг обеспечивает наилучшую производительность, если сортировка является обязательной (00: 00: 05.85)
SELECT pid, LISTAGG(Desc, ' ') WITHIN GROUP (ORDER BY seq) AS description FROM B GROUP BY pid;
COLLECT обеспечивает наилучшую производительность, если сортировка не требуется (00: 00: 02.90):
SELECT pid, TO_STRING(CAST(COLLECT(Desc) AS varchar2_ntt)) AS Vals FROM B GROUP BY pid;
Собирать с помощью заказа бит медленнее (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
Синтаксис заголовка функции может быть неправильным, но принцип работает.