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 中的聚合字符串连接 [重复]的主要内容,如果未能解决你的问题,请参考以下文章

如何连接具有选择性重复记录的表? (oracle10g)

在 Oracle 10g 上创建聚合函数返回无用的错误

Oracle 10g 从两个不同的行和列聚合成一行

创建带有外键的表会出现错误 ORA-00904: : oracle 10g 中的无效标识符 [重复]

Classic ADO / ODBC / Oracle 10g EZConnect 的连接字符串语法

如何在 CLOB 字段(Oracle 10g)中快速替换出现极高的相同字符?