Oracle 10g 中的聚合字符串连接 [重复]
Posted
技术标签:
【中文标题】Oracle 10g 中的聚合字符串连接 [重复]【英文标题】:Aggregate string connection in Oracle 10g [duplicate] 【发布时间】:2013-11-25 11:06:14 【问题描述】:我看到了上一个问题,其中表格包含“否”和“名称”列,以及与数字列分组的其他列,但无法实现为我的案例提供的答案。我需要做同样的事情,但使用非数字分组。源表是包含这些列的 tbl1:
POD Name
--- -----
North Rony
North James
North Aby
South Sam
South Willy
West Mike
我需要做这个聚合:
POD Name
--- -----
North Aby,James,Rony
South Sam,Willy
West Mike
由于“POD”是非数字的,Msyma、Dinup 和 chetan 之前的解决方案似乎对我不起作用。
我不知道如何将知识从他们的答案转移到这些要求。
理想的查询应该是
SELECT POD, AGGREGATESTRING(Name)
FROM tbl1
GROUP BY POD
在理想示例中,AGGREGATESTRING 不会对人名进行排序,但我想我可以在需要的地方插入“ORDER BY”。
【问题讨论】:
GROUP_CONCAT 在 Oracle 上不存在。 【参考方案1】:Oracle 11g 有这个简洁的函数 LISTAGG,这几乎是您想要的,但是由于您使用的是 10g,因此您无法使用它(除非您决定升级)。
如果由于某种原因您不希望(或由于任何原因不能)升级到 11g,我建议您查看 10g 上可用的 LISTAGG 的一些替代品。
您可以查看一些建议的替代方案here
快速调整对建议替代方案之一的快速调整以匹配您的案例场景:
WITH Q AS
(
SELECT 'North' POD, 'Rony' NAME FROM DUAL UNION ALL
SELECT 'North', 'James' FROM DUAL UNION ALL
SELECT 'North', 'Aby' FROM DUAL UNION ALL
SELECT 'South', 'Sam' FROM DUAL UNION ALL
SELECT 'South', 'Willy' FROM DUAL UNION ALL
SELECT 'West', 'Mike' FROM DUAL
)
SELECT POD,
RTRIM(
XMLAGG (XMLELEMENT(e, name||',') ORDER BY name).EXTRACT('//text()'),
','
) AS name
FROM q
GROUP BY POD;
但请记住,这不是实际的解决方案,因为您必须根据您的表(而不是虚拟 DUAL 表)等来定制它......
您的解决方案可能类似于以下内容:
SELECT POD,
RTRIM(
XMLAGG (XMLELEMENT(E, NAME||',') ORDER BY NAME).EXTRACT('//text()'),
','
) AS NAME
FROM tbl1
GROUP BY POD;
如果你想改变分隔符,你可以在这部分用逗号改变它:
(E, NAME||',')
RTRIM 只是为了从连接字符串的末尾剪掉尾随逗号,如果您不被尾随逗号困扰,您可以省略 RTRIM 函数以保持可读性。
【讨论】:
+1 很好地使用了与 XML 相关的功能。但是,如果连接的值超过 4000 个字符,此解决方案将引发 ORA-19011。如果您需要更多,则必须编写自己的聚合函数来处理。 准确地说:listagg
是在 Oracle 11 Release 2(又名 11.2)中引入的,11.1 没有 listag
【参考方案2】:
还有一种方式 WM_CONCAT
with Q as
(select 'North' POD, 'Rony' name
from DUAL
union all
select 'North', 'James'
from DUAL
union all
select 'North', 'Aby'
from DUAL
union all
select 'South', 'Sam'
from DUAL
union all
select 'South', 'Willy'
from DUAL
union all
select 'West', 'Mike' from DUAL)
select pod, to_char(wm_concat(name)) as name from q group by pod
分层查询的字符串聚合
with Q as
(select 'North' POD, 'Rony' name
from DUAL
union all
select 'North', 'James'
from DUAL
union all
select 'North', 'Aby'
from DUAL
union all
select 'South', 'Sam'
from DUAL
union all
select 'South', 'Willy'
from DUAL
union all
select 'West', 'Mike' from DUAL)
select pod, group_name
from (select t1.*, level as lv, substr(sys_connect_by_path(name, ','), 2) as group_name
from (select t1.*, nvl(lag(row_rank) over(partition by pod order by row_rank), 0) as parent_row_rank
from (select q.*,
rank() over(partition by pod order by name) as row_rank,
rank() over(partition by pod order by name desc) as row_rank_desc
from q) t1) t1
where row_rank_desc = 1
connect by prior row_rank = parent_row_rank
and prior pod = pod
start with parent_row_rank = 0) t1
【讨论】:
请注意,WM_CONCAT 是一个不受支持的函数 @RobvanWijk 是的,但它的工作;) WM_CONCAT 不仅是有效的方法之一,而且不在官方文档中。它确实不适用于很多系统。我使用的所有数据库都以ORA-00904: "WM_CONCAT": invalid identifier
失败。 (不知道为什么我们删除了 Workspace Manager - 可能是一些奇怪的许可或安全问题?)此外,它也不适用于 Express Edition。
@jonearles 所以看起来我很幸运)检查字符串聚合的一条路径(漫长而痛苦)(我正在编辑答案)【参考方案3】:
我只能在 Oracle 11g R2 上进行测试;不过,我相信一切都适用于 Oracle 10g。
这里包含两个函数,它们都使用集合:
第一个功能比较简单; 第二个函数使用了DBMS_LOB
包,并且更加冗长,但在我的测试中,它似乎效率更高(尽管我建议您自己对其进行分析以进行测试)。
SQL Fiddle
Oracle 11g R2 架构设置:
对于此方法,您需要定义一个 Collection 来将字符串聚合到:
CREATE OR REPLACE TYPE VARCHAR2s_Table AS TABLE OF VARCHAR2(4000);
/
这个函数接受一个字符串集合(和一个可选的分隔符)并返回一个包含连接字符串的CLOB
- 如果您有一个较小的数据集(根据您的示例),那么这可能是矫枉过正,您可以替换CLOB
VARCHAR2
。
CREATE OR REPLACE FUNCTION concatStrings(
Strs VARCHAR2s_Table,
delim VARCHAR2 DEFAULT ','
) RETURN CLOB
AS
out_string CLOB;
BEGIN
FOR i IN 1 .. Strs.COUNT LOOP
out_string := out_string || CASE WHEN i = 1 THEN '' ELSE delim END || Strs(i);
END LOOP;
RETURN out_string;
END;
/
但是,如果您要以CLOB
的形式返回一个长字符串,那么使用DBMS_LOB
包的某些功能可能会更有效:
CREATE OR REPLACE FUNCTION concatStrings2(
Strs VARCHAR2s_Table,
delim VARCHAR2 DEFAULT ','
) RETURN CLOB
AS
out_string CLOB;
dl CONSTANT NUMBER(10) := LENGTH( delim );
BEGIN
DBMS_LOB.CREATETEMPORARY( out_string, TRUE );
IF strs IS NOT NULL AND strs IS NOT EMPTY THEN
IF dl > 0 THEN
DBMS_LOB.WRITEAPPEND( out_string, LENGTH( strs(1) ), strs(1) );
FOR i IN 2 .. strs.COUNT LOOP
DBMS_LOB.WRITEAPPEND( out_string, dl, delim );
DBMS_LOB.WRITEAPPEND( out_string, LENGTH( strs(i) ), strs(i) );
END LOOP;
ELSE
FOR i IN 1 .. strs.COUNT LOOP
DBMS_LOB.WRITEAPPEND( out_string, LENGTH( strs(i) ), strs(i) );
END LOOP;
END IF;
END IF;
RETURN out_string;
END concatStrings2;
/
您的测试数据:
CREATE TABLE tbl1 ( POD, name ) AS
SELECT 'North', 'Rony' FROM DUAL
UNION ALL SELECT 'North', 'James' FROM DUAL
UNION ALL SELECT 'North', 'Aby' FROM DUAL
UNION ALL SELECT 'South', 'Sam' FROM DUAL
UNION ALL SELECT 'South', 'Willy' FROM DUAL
UNION ALL SELECT 'West', 'Mike' FROM DUAL
/
查询 1:
SELECT POD,
concatStrings( CAST( COLLECT( name ORDER BY name ASC ) AS VARCHAR2s_Table )) AS name
FROM tbl1
GROUP BY POD
Results:
| POD | NAME |
|-------|----------------|
| North | Aby,James,Rony |
| South | Sam,Willy |
| West | Mike |
查询 2:
SELECT POD,
concatStrings2( CAST( COLLECT( name ORDER BY name ASC ) AS VARCHAR2s_Table )) AS name
FROM tbl1
GROUP BY POD
Results:
| POD | NAME |
|-------|----------------|
| North | Aby,James,Rony |
| South | Sam,Willy |
| West | Mike |
【讨论】:
【参考方案4】: SELECT POD,WM_CONCAT(NAME) AS AGG_STRING FROM TEST_AV
GROUP BY POD;
----------------------------------------------------------------------------------
POD AGG_STRING
NORTH JANES,RONY,ABY
SOUTH WILLY
WEST MIKE
3 rows returned in 0.07 seconds
【讨论】:
以上是关于Oracle 10g 中的聚合字符串连接 [重复]的主要内容,如果未能解决你的问题,请参考以下文章
创建带有外键的表会出现错误 ORA-00904: : oracle 10g 中的无效标识符 [重复]