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 ответов:
есть несколько способов, в зависимости от того, какая версия у вас - вижу документация 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.1ORA-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
LISTAGG обеспечивает лучшую производительность, если сортировка является обязательным(00: 00: 05.85)
SELECT pid, LISTAGG(Desc, ' ') WITHIN GROUP (ORDER BY seq) AS description FROM B GROUP BY pid;
сбор обеспечивает лучшую производительность, если сортировка не требуется(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
синтаксис заголовка функции может быть неправильным, но принцип работает.