SQL Query用于连接Oracle中多行的列值

时间:2011-01-13 23:34:34

标签: sql oracle string-aggregation

是否可以构造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列基本上是表B中SEQ值的串联?

有关SQL的任何帮助吗?

11 个答案:

答案 0 :(得分:196)

根据您的版本,有几种方法 - 请参阅oracle documentation on string aggregation techniques。一个非常常见的方法是使用LISTAGG

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

然后加入A,挑选出您想要的pids

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

答案 1 :(得分:19)

还有一个XMLAGG函数,适用于11.2之前的版本。由于WM_CONCATundocumented and unsupported by Oracle,因此建议不要在生产系统中使用它。

使用XMLAGG,您可以执行以下操作:

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

这是做什么的

  • ename列的值(用逗号连接)放在xml元素中的employee_names表中(带标记E)
  • 摘录本
  • 的文字
  • 聚合xml(连接它)
  • 调用结果列“结果”

答案 2 :(得分:11)

使用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.

我写了这篇here。如果你按照OTN线程的链接,你会发现更多,包括性能比较。

答案 3 :(得分:8)

Oracle 11g第2版 中引入了 LISTAGG 分析功能,这使得汇总字符串变得非常容易。 如果您使用的是11g第2版,则应使用此函数进行字符串聚合。 有关字符串连接的更多信息,请参阅以下网址。

http://www.oracle-base.com/articles/misc/StringAggregationTechniques.php

String Concatenation

答案 4 :(得分:7)

正如大多数答案所示,LISTAGG是明显的选择。但是,LISTAGG的一个令人烦恼的方面是,如果连接字符串的总长度超过4000个字符(SQL中VARCHAR2的限制),则抛出以下错误,这在Oracle版本中很难管理12.1

  

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

12cR2中添加的新功能是ON OVERFLOW的{​​{1}}子句。 包含此子句的查询如下所示:

LISTAGG

以上将输出限制为4000个字符,但不会引发SELECT pid, LISTAGG(Desc, ' ' on overflow truncate) WITHIN GROUP (ORDER BY seq) AS desc FROM B GROUP BY pid; 错误。

这些是ORA-01489子句的一些附加选项:

  • ON OVERFLOW:这会显示ON OVERFLOW TRUNCATE 'Contd..' 字符串的结尾(默认为'Contd..'
  • ...:这将显示4000个字符 没有任何终止字符串。
  • ON OVERFLOW TRUNCATE '':这将显示总数 终止字符后的结尾处的字符数。 例如: - ' ON OVERFLOW TRUNCATE WITH COUNT'
  • ...(5512):如果您希望ON OVERFLOW ERROR失败 LISTAGG错误(无论如何都是默认值)。

答案 5 :(得分:5)

对于那些必须使用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子句中重新排序各个连接描述,并且可以在最终的ORDER BY子句中重新排序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;

可以在PARTITION BY子句中重新排序单个连接描述。

此页面上的其他几个答案也提到了这个非常有用的参考: https://oracle-base.com/articles/misc/string-aggregation-techniques

答案 6 :(得分:2)

  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. 订购的COLLECT有点慢(00:00:07.08):

    SELECT pid, TO_STRING(CAST(COLLECT(Desc ORDER BY Desc) AS varchar2_ntt)) AS Vals FROM B GROUP BY pid;

  4. 所有其他技术都比较慢。

答案 7 :(得分:1)

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

SET SERVEROUT ON SIZE 6000

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

答案 8 :(得分:0)

我使用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;

答案 9 :(得分:-1)

试试这段代码:

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

答案 10 :(得分:-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

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