是否可以构造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的任何帮助吗?
答案 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_CONCAT
为undocumented and unsupported by Oracle,因此建议不要在生产系统中使用它。
使用XMLAGG
,您可以执行以下操作:
SELECT XMLAGG(XMLELEMENT(E,ename||',')).EXTRACT('//text()') "Result"
FROM employee_names
这是做什么的
ename
列的值(用逗号连接)放在xml元素中的employee_names
表中(带标记E)答案 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
答案 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)
如果必须进行分类,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;
订购的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;
所有其他技术都比较慢。
答案 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
函数头语法可能有误,但原理确实有效。