SQL查询以串联Oracle中多个行的列值


169

可以构造SQL来连接多行中的列值吗?

以下是一个示例:

表A

PID
一个
乙
C

表B

PID序列描述

A 1有
A 2不错
3天。
B 1做得好。
C 1是
C 2我们可以 
C 3做 
C 4这项工作!

SQL的输出应为-

PID描述
A祝你有美好的一天。
B做得好。
C是的,我们可以做这项工作!

因此,基本上,输出表的Desc列是表B中SEQ值的串联?

对SQL有帮助吗?



请看看这个解决方案。这将对您有用。
Jineesh Uvantavida

Answers:


237

有几种方法取决于您使用的版本-请参阅有关字符串聚合技术oracle文档。一种很常见的用法是LISTAGG

SELECT pid, LISTAGG(Desc, ' ') WITHIN GROUP (ORDER BY seq) AS description
FROM B GROUP BY pid;

然后加入以A挑选pids您想要的。

注意:开箱即用,LISTAGG仅适用于VARCHAR2列。


2
在Oracle 10g中使用wm_concat()可以按逗号分隔的序列号的升序连接文本,我们可以使降序由其他分隔吗?
jagamot 2011年

19

还有一个XMLAGG功能,可用于11.2之前的版本。因为WM_CONCAT无证和不支持的Oracle,建议不要在生产系统中使用它。

有了XMLAGG你可以做到以下几点:

SELECT XMLAGG(XMLELEMENT(E,ename||',')).EXTRACT('//text()') "Result" 
FROM employee_names

这是什么

  • 将表中的ename列(用逗号连接)的值放在employee_namesxml元素(带有标签E)中
  • 提取此文本
  • 聚合xml(将其连接)
  • 将结果列称为“结果”

XMLAGG适用于Oracle 12.2。而且,XLMAGG可以连接非常长的字符串,而LISTAGG可能由于它们的最终长度而无法连接。
马可(Marco)

13

使用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线程的链接,则会发现更多内容,包括性能比较。



8

正如大多数答案所暗示的,这LISTAGG是显而易见的选择。但是,一个令人讨厌的方面LISTAGG是,如果串联字符串的总长度超过4000个字符(VARCHAR2SQL中的限制),则会引发以下错误,这在Oracle 12.1版之前的版本中很难管理。

ORA-01489:字符串连接的结果太长

ON OVERFLOW子句是12cR2中添加的新功能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错误(始终为默认值)。

6

对于那些必须使用Oracle 9i(或更早版本)解决此问题的人,您可能需要使用SYS_CONNECT_BY_PATH,因为LISTAGG不可用。

为了回答OP,以下查询将显示表A中的PID并连接表B中的所有DESC列:

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子句中重新排序,而PID列表可以在最终的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


3
  1. 如果必须进行排序,LISTAGG可提供最佳性能(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;

所有其他技术都比较慢。


1
详细说明您的答案会有所帮助。
乔恩·瑟雷尔

John,我不想在本文中重复,但总而言之,这些是以下结果:1.如果必须进行排序,则LISTAGG可以提供最佳性能(00:00:05.85)2.如果不进行排序,则COLLECT可以提供最佳性能needed(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; 所有其他技术都比较慢。
Misho 2015年

1
您可以只编辑答案以包括相关信息。
乔恩·瑟雷尔

我在编辑中为时已晚,这就是为什么我再次添加它。抱歉,我是新来的,刚开始了解它。
Misho 2015年

1

在运行选择查询之前,请运行以下命令:

SET SERVEROUT ON SIZE 6000

SELECT XMLAGG(XMLELEMENT(E,SUPLR_SUPLR_ID||',')).EXTRACT('//text()') "SUPPLIER" 
FROM SUPPLIERS;


-3

在选择要串联的位置,调用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

函数头语法可能是错误的,但是该原理确实有效。


这对于Oracle是无效的
a_horse_with_no_name 16'Nov
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.