Oracle 10g中的聚合字符串连接

时间:2013-11-25 11:06:15

标签: sql oracle oracle10g aggregate-functions

我看到上一个问题,其中表格中包含“No”和“Name”列,以及其他与数字列组合在一起但却无法实现为我的案例提供的答案的问题。我需要做同样的事情,但是使用非数字分组。源表是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”。

4 个答案:

答案 0 :(得分:2)

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

答案 2 :(得分:0)

我只能在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 - 如果您有一个较小的数据集(根据您的示例),那么这可能是过度的,您可以替换{ {1}} 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;
/

查询1

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
/

<强> Results

SELECT POD,
       concatStrings( CAST( COLLECT( name ORDER BY name ASC ) AS VARCHAR2s_Table )) AS name
FROM   tbl1
GROUP BY POD

查询2

|   POD |           NAME |
|-------|----------------|
| North | Aby,James,Rony |
| South |      Sam,Willy |
|  West |           Mike |

<强> Results

SELECT POD,
       concatStrings2( CAST( COLLECT( name ORDER BY name ASC ) AS VARCHAR2s_Table )) AS name
FROM   tbl1
GROUP BY POD

答案 3 :(得分:-1)

    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