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