Oracle中的LISTAGG返回不同的值


94

我正在尝试LISTAGG在Oracle中使用该功能。我只想获取该列的不同值。有没有一种方法可以只获取不同的值而无需创建函数或过程?

  col1 col2创建者
   1 2史密斯 
   1 2约翰 
   1 3阿杰 
   1 4羊 
   1 5杰克 

我需要选择col1和LISTAGGcol2(不考虑第3列)。当我这样做时,我得到如下结果LISTAGG[2,2,3,4,5]

我需要在此处删除重复的“ 2”;我只需要col2对col1的不同值。



您可以显示样本中的预期输出(行)吗?您想知道col1是否有多个值?
a_horse_with_no_name 2012年

LISTAGG的预期输出为[2,3,4,5]。第二个“ 2”应删除。而且我的表有1000多行。
Priyanth

您想知道col1是否有多个值?
a_horse_with_no_name 2012年

代码是这样的:-从表T WHERE ...中选择SELECT col1,LISTAGG(col2,',')在组中(按col2排序)...。因此,它应显示与col1对应的col2的所有不同值,并用逗号。
普里扬斯

Answers:


77

19c及更高版本:

select listagg(distinct the_column, ',') within group (order by the_column)
from the_table

18c及更早版本:

select listagg(the_column, ',') within group (order by the_column)
from (
   select distinct the_column 
   from the_table
) t

如果您需要更多列,则可能需要以下内容:

select col1, listagg(col2, ',') within group (order by col2)
from (
  select col1, 
         col2,
         row_number() over (partition by col1, col2 order by col1) as rn
  from foo
  order by col1,col2
)
where rn = 1
group by col1;

2
类似于我的想法。如果listagg是查询中唯一的聚合函数,则应该这样做。但是,将其与其他聚合函数结合起来会更加棘手。
Andriy M

是。我的查询与此类似。
Priyanth

1
@a_horse_with_no_name:上面的select语句为我提供了重复的值。我要删除重复项。col1 col2由1 2 Smith 1 2 John 1 3 Ajay 1 4 Ram 1 5 Jack创建我需要选择col1和col2的LISTAGG(不考虑第3列)。当我这样做时,我将得到类似LISTAGG的结果:-> [2,2,3,4,5]我需要在这里删除重复的'2'。我只需要对col1的col2的不同值。
Priyanth

@a_horse_with_no_name:我尝试执行代码-并收到如下错误消息ORA-01489:字符串连接的结果太长01489。00000-“字符串连接的结果太长” *原因:字符串连接的结果大于最大值尺寸。
Priyanth

@Priyanth:那你真倒霉。总长度超过4000字节,Oracle无法处理。您将需要在应用程序代码中进行汇总。
a_horse_with_no_name 2012年

47

这是解决问题的方法。

select  
      regexp_replace(
    '2,2,2.1,3,3,3,3,4,4' 
     ,'([^,]+)(,\1)*(,|$)', '\1\3')

from dual

退货

2,2.1,3,4

从oracle 19C内置于此处

从18C开始,更早在小组内尝试 这里

否则使用正则表达式

下面的答案:

select col1, 

regexp_replace(
    listagg(
     col2 , ',') within group (order by col2)  -- sorted
    ,'([^,]+)(,\1)*(,|$)', '\1\3') )
   from tableX
where rn = 1
group by col1; 

注意:上面的代码在大多数情况下都可以使用-列表应该排序,您可能必须根据数据修剪所有尾随和前导的空格。

如果组中有很多项目> 20个或较大的字符串大小,则可能会遇到oracle字符串大小限制“字符串连接的结果太长”。

从oracle 12cR2您可以抑制此错误,请参见此处。或者,为每个组中的成员设置一个最大数量。只有在可以只列出第一个成员的情况下,这才起作用。如果可变字符串很长,则可能无法正常工作。您将不得不尝试。

select col1,

case 
    when count(col2) < 100 then 
       regexp_replace(
        listagg(col2, ',') within group (order by col2)
        ,'([^,]+)(,\1)*(,|$)', '\1\3')

    else
    'Too many entries to list...'
end

from sometable
where rn = 1
group by col1;

另一种解决方案(没那么简单),希望能够避免oracle的字符串大小限制-字符串大小限制为4000感谢这个职位在这里通过user3465996

select col1  ,
    dbms_xmlgen.convert(  -- HTML decode
    dbms_lob.substr( -- limit size to 4000 chars
    ltrim( -- remove leading commas
    REGEXP_REPLACE(REPLACE(
         REPLACE(
           XMLAGG(
             XMLELEMENT("A",col2 )
               ORDER BY col2).getClobVal(),
             '<A>',','),
             '</A>',''),'([^,]+)(,\1)*(,|$)', '\1\3'),
                  ','), -- remove leading XML commas ltrim
                      4000,1) -- limit to 4000 string size
                      , 1)  -- HTML.decode
                       as col2
 from sometable
where rn = 1
group by col1;

V1-一些测试用例-仅供参考

regexp_replace('2,2,2.1,3,3,4,4','([^,]+)(,\1)+', '\1')
-> 2.1,3,4 Fail
regexp_replace('2 ,2 ,2.1,3 ,3 ,4 ,4 ','([^,]+)(,\1)+', '\1')
-> 2 ,2.1,3,4 Success  - fixed length items

V2项目包含在项目中,例如 2,21

regexp_replace('2.1,1','([^,]+)(,\1)+', '\1')
-> 2.1 Fail
regexp_replace('2 ,2 ,2.1,1 ,3 ,4 ,4 ','(^|,)(.+)(,\2)+', '\1\2')
-> 2 ,2.1,1 ,3 ,4  -- success - NEW regex
 regexp_replace('a,b,b,b,b,c','(^|,)(.+)(,\2)+', '\1\2')
-> a,b,b,c fail!

v3-正则表达式感谢Igor!适用于所有情况。

select  
regexp_replace('2,2,2.1,3,3,4,4','([^,]+)(,\1)*(,|$)', '\1\3') ,
---> 2,2.1,3,4 works
regexp_replace('2.1,1','([^,]+)(,\1)*(,|$)', '\1\3'),
--> 2.1,1 works
regexp_replace('a,b,b,b,b,c','([^,]+)(,\1)*(,|$)', '\1\3')
---> a,b,c works

from dual

3
公平的结果,但并非如此简单。如果数据大小很大,您会遇到ORA-01489: result of string concatenation is too long
佩罗2014年

1
我不会称其为简单但非常有吸引力的解决方案。我不知道可以在搜索字符串中使用匹配号,而不仅仅是替换字符串。太好了
彼得·克拉索伊

1
请注意,此方法要求对值进行排序,以便重复的值是连续的。否则,它将失败。但是简单就好!我正在针对我的特殊情况使用此方法。谢谢!
StewS2 '16

2
超级简单不适用于3次以上的重复!,例如a,b,b,b,b,c将变为a,b,b,c:-((Oracle 11.2)
Andreas Dietrich

4
@AndreasDietrich-以下解决方案似乎总是正确的:regexp_replace(your_string, '([^,]+)(,\1)*(,|$)', '\1\3')
Egor Skriptunoff 2016年

10

您可以使用未记录的wm_concat功能。

select col1, wm_concat(distinct col2) col2_list 
from tab1
group by col1;

此函数返回clob列,如果需要,可以使用dbms_lob.substr将clob转换为varchar2。


15
不,不要使用它。
越志

1
这正是我所需要的,并且可以在我现有的聚合查询中完美地工作,而不是将该查询包装在外部查询中。使用有什么问题wm_concat(distinct x)
Ehryk 2015年

1
因为它没有记录,并且在12c中不存在。但是无论如何,我认为这是最好的方法。
KemalettinErbakırcı2015年

1
谢谢@kemalettinerbakırcı!@thg您应该考虑,如果未记录某些东西,您将不知道它的副作用是什么,以及《文档》告诉您有关已记录函数的任何其他信息;您只是将其用作黑匣子,并且您只知道哪个杠杆根据民俗来做什么。
Koshinae

4
切勿使用wm_concat。请参阅为什么不在Oracle中使用WM_CONCAT函数?
Lalit Kumar B

7

我通过首先对值进行分组来克服了这个问题,然后使用listagg进行了另一个聚合。像这样:

select a,b,listagg(c,',') within group(order by c) c, avg(d)
from (select a,b,c,avg(d)
      from   table
      group by (a,b,c))
group by (a,b)

仅一个全表访问,相对容易扩展到更复杂的查询


6

如果打算将此转换应用于多个列,我扩展了a_horse_with_no_name的解决方案:

SELECT * FROM
(SELECT LISTAGG(GRADE_LEVEL, ',') within group(order by GRADE_LEVEL) "Grade Levels" FROM (select distinct GRADE_LEVEL FROM Students) t)                     t1,
(SELECT LISTAGG(ENROLL_STATUS, ',') within group(order by ENROLL_STATUS) "Enrollment Status" FROM (select distinct ENROLL_STATUS FROM Students) t)          t2,
(SELECT LISTAGG(GENDER, ',') within group(order by GENDER) "Legal Gender Code" FROM (select distinct GENDER FROM Students) t)                               t3,
(SELECT LISTAGG(CITY, ',') within group(order by CITY) "City" FROM (select distinct CITY FROM Students) t)                                                  t4,
(SELECT LISTAGG(ENTRYCODE, ',') within group(order by ENTRYCODE) "Entry Code" FROM (select distinct ENTRYCODE FROM Students) t)                             t5,
(SELECT LISTAGG(EXITCODE, ',') within group(order by EXITCODE) "Exit Code" FROM (select distinct EXITCODE FROM Students) t)                                 t6,
(SELECT LISTAGG(LUNCHSTATUS, ',') within group(order by LUNCHSTATUS) "Lunch Status" FROM (select distinct LUNCHSTATUS FROM Students) t)                     t7,
(SELECT LISTAGG(ETHNICITY, ',') within group(order by ETHNICITY) "Race Code" FROM (select distinct ETHNICITY FROM Students) t)                              t8,
(SELECT LISTAGG(CLASSOF, ',') within group(order by CLASSOF) "Expected Graduation Year" FROM (select distinct CLASSOF FROM Students) t)                     t9,
(SELECT LISTAGG(TRACK, ',') within group(order by TRACK) "Track Code" FROM (select distinct TRACK FROM Students) t)                                         t10,
(SELECT LISTAGG(GRADREQSETID, ',') within group(order by GRADREQSETID) "Graduation ID" FROM (select distinct GRADREQSETID FROM Students) t)                 t11,
(SELECT LISTAGG(ENROLLMENT_SCHOOLID, ',') within group(order by ENROLLMENT_SCHOOLID) "School Key" FROM (select distinct ENROLLMENT_SCHOOLID FROM Students) t)       t12,
(SELECT LISTAGG(FEDETHNICITY, ',') within group(order by FEDETHNICITY) "Federal Race Code" FROM (select distinct FEDETHNICITY FROM Students) t)                         t13,
(SELECT LISTAGG(SUMMERSCHOOLID, ',') within group(order by SUMMERSCHOOLID) "Summer School Key" FROM (select distinct SUMMERSCHOOLID FROM Students) t)                               t14,
(SELECT LISTAGG(FEDRACEDECLINE, ',') within group(order by FEDRACEDECLINE) "Student Decl to Prov Race Code" FROM (select distinct FEDRACEDECLINE FROM Students) t)          t15

这是Oracle Database 11g企业版11.2.0.2.0版-64位生产。
我无法使用STRAGG,因为无法进行DISTINCT和ORDER。

性能可以线性扩展,这很好,因为我要添加所有感兴趣的列。上面花了3秒钟完成了77K行。只需汇总1.172秒。我这样做是有一种方法可以一次通过表格来区分表中的多个列。


6

如果要在MULTIPLE列中使用不同的值,想要控制排序顺序,不想使用可能会消失的未记录功能,并且不希望进行多次全表扫描,则可能会发现此构造很有用:

with test_data as 
(
      select 'A' as col1, 'T_a1' as col2, '123' as col3 from dual
union select 'A', 'T_a1', '456' from dual
union select 'A', 'T_a1', '789' from dual
union select 'A', 'T_a2', '123' from dual
union select 'A', 'T_a2', '456' from dual
union select 'A', 'T_a2', '111' from dual
union select 'A', 'T_a3', '999' from dual
union select 'B', 'T_a1', '123' from dual
union select 'B', 'T_b1', '740' from dual
union select 'B', 'T_b1', '846' from dual
)
select col1
     , (select listagg(column_value, ',') within group (order by column_value desc) from table(collect_col2)) as col2s
     , (select listagg(column_value, ',') within group (order by column_value desc) from table(collect_col3)) as col3s
from 
(
select col1
     , collect(distinct col2) as collect_col2
     , collect(distinct col3) as collect_col3
from test_data
group by col1
);

1
如果将“ union”替换为“ union all”,则可以节省更多时间。
burkay

4

如何创建一个专用功能,使“独特的”部分成为可能:

create or replace function listagg_distinct (t in str_t, sep IN VARCHAR2 DEFAULT ',') 
  return VARCHAR2
as 
  l_rc VARCHAR2(4096) := '';
begin
  SELECT listagg(val, sep) WITHIN GROUP (ORDER BY 1)
    INTO l_rc
    FROM (SELECT DISTINCT column_value val FROM table(t));
  RETURN l_rc;
end;
/

然后使用它进行聚合:

SELECT col1, listagg_distinct(cast(collect(col_2) as str_t ), ', ')
  FROM your_table
  GROUP BY col_1;

4

为了解决字符串长度问题,您可以使用XMLAGG类似于listagg但它会返回Clob。

然后,您可以使用解析regexp_replace并获取唯一值,然后使用将其转换为字符串dbms_lob.substr()。如果您有大量不同的值,您仍然会以这种方式用尽空间,但是在很多情况下,下面的代码应该可以工作。

您也可以更改使用的定界符。就我而言,我想用“-”代替“”,但是您应该能够替换代码中的破折号,并在需要时使用逗号。

select col1,
    dbms_lob.substr(ltrim(REGEXP_REPLACE(REPLACE(
         REPLACE(
           XMLAGG(
             XMLELEMENT("A",col2)
               ORDER BY col2).getClobVal(),
             '<A>','-'),
             '</A>',''),'([^-]*)(-\1)+($|-)', 
           '\1\3'),'-'), 4000,1) as platform_mix
from table

这是一个好主意,需要调用dbms_xmlgen.convert(string,1)删除和&->&amp转换。查看我的帖子链接
ozmike 2015年

3

使用DECODE vs CASE进一步完善@YoYo对基于@a_horse_with_no_name的row_number()的方法的校正(我在这里看到了)。我看到@Martin Vrbovsky也有此案例方法的答案。

select
  col1, 
  listagg(col2, ',') within group (order by col2) AS col2_list,
  listagg(col3, ',') within group (order by col3) AS col3_list,
  SUM(col4) AS col4
from (
  select
    col1, 
    decode(row_number() over (partition by col1, col2 order by null),1,col2) as col2,
    decode(row_number() over (partition by col1, col3 order by null),1,col3) as col3
  from foo
)
group by col1;

2

即将推出的甲骨文19C将支持DISTINCTLISTAGG

带有DISTINCT选项的LISTAGG

此功能随19c一起提供:

SQL> select deptno, listagg (distinct sal,', ') within group (order by sal)  
  2  from scott.emp  
  3  group by deptno;  

编辑:

甲骨文19C LISTAGG DISTINCT

现在,LISTAGG聚合函数通过使用新的DISTINCT关键字来支持重复消除。LISTAGG聚合函数根据ORDER BY表达式对查询中每个组的行进行排序,然后将值连接为单个字符串。使用新的DISTINCT关键字,可以在连接成单个字符串之前从指定的表达式中删除重复的值。这消除了在使用聚合LISTAGG函数之前创建复杂查询处理以查找不同值的需要。使用DISTINCT选项,可以直接在LISTAGG函数中完成删除重复值的处理。结果是更简单,更快,更有效的SQL。


0

有没有人考虑使用PARTITION BY子句?它在此查询中对我有用,以获得应用程序服务和访问的列表。

SELECT DISTINCT T.APP_SVC_ID, 
       LISTAGG(RTRIM(T.ACCESS_MODE), ',') WITHIN GROUP(ORDER BY T.ACCESS_MODE) OVER(PARTITION BY T.APP_SVC_ID) AS ACCESS_MODE 
  FROM APP_SVC_ACCESS_CNTL T 
 GROUP BY T.ACCESS_MODE, T.APP_SVC_ID

我不得不删掉NDA的where子句,但是您明白了。


我不了解此查询如何为接受不同的项目LISTAGG。看来,T.ACCESS_MODE由于您要按行分组,因此每行只能有一个?
jpmc26 2015年

0

我认为这可能会有所帮助-如果列值重复则将列值设置为NULL-然后不将其附加到LISTAGG字符串中:

with test_data as 
(
      select 1 as col1, 2 as col2, 'Smith' as created_by from dual
union select 1, 2, 'John' from dual
union select 1, 3, 'Ajay' from dual
union select 1, 4, 'Ram' from dual
union select 1, 5, 'Jack' from dual
union select 2, 5, 'Smith' from dual
union select 2, 6, 'John' from dual
union select 2, 6, 'Ajay' from dual
union select 2, 6, 'Ram' from dual
union select 2, 7, 'Jack' from dual
)
SELECT col1  ,
      listagg(col2 , ',') within group (order by col2 ASC) AS orig_value,
      listagg(CASE WHEN rwn=1 THEN col2 END , ',') within group (order by col2 ASC) AS distinct_value
from 
    (
    select row_number() over (partition by col1,col2 order by 1) as rwn, 
           a.*
    from test_data a
    ) a
GROUP BY col1   

结果是:

COL1  ORIG         DISTINCT
1   2,2,3,4,5   2,3,4,5
2   5,6,6,6,7   5,6,7

0

listagg()忽略NULL值,因此在第一步中,您可以使用lag()函数来分析先前的记录是否具有相同的值,如果是,则为NULL,否则为“新值”。

WITH tab AS 
(           
          SELECT 1 as col1, 2 as col2, 'Smith' as created_by FROM dual
UNION ALL SELECT 1 as col1, 2 as col2, 'John'  as created_by FROM dual
UNION ALL SELECT 1 as col1, 3 as col2, 'Ajay'  as created_by FROM dual
UNION ALL SELECT 1 as col1, 4 as col2, 'Ram'   as created_by FROM dual
UNION ALL SELECT 1 as col1, 5 as col2, 'Jack'  as created_by FROM dual
)
SELECT col1
     , CASE 
       WHEN lag(col2) OVER (ORDER BY col2) = col2 THEN 
         NULL 
       ELSE 
         col2 
       END as col2_with_nulls
     , created_by
  FROM tab;

结果

      COL1 COL2_WITH_NULLS CREAT
---------- --------------- -----
         1               2 Smith
         1                 John
         1               3 Ajay
         1               4 Ram
         1               5 Jack

请注意,第二个2被NULL取代。现在,您可以将SELECT与其周围的listagg()一起包装起来。

WITH tab AS 
(           
          SELECT 1 as col1, 2 as col2, 'Smith' as created_by FROM dual
UNION ALL SELECT 1 as col1, 2 as col2, 'John'  as created_by FROM dual
UNION ALL SELECT 1 as col1, 3 as col2, 'Ajay'  as created_by FROM dual
UNION ALL SELECT 1 as col1, 4 as col2, 'Ram'   as created_by FROM dual
UNION ALL SELECT 1 as col1, 5 as col2, 'Jack'  as created_by FROM dual
)
SELECT listagg(col2_with_nulls, ',') WITHIN GROUP (ORDER BY col2_with_nulls) col2_list
  FROM ( SELECT col1
              , CASE WHEN lag(col2) OVER (ORDER BY col2) = col2 THEN NULL ELSE col2 END as col2_with_nulls
              , created_by
           FROM tab );

结果

COL2_LIST
---------
2,3,4,5

您也可以在多列中执行此操作。

WITH tab AS 
(           
          SELECT 1 as col1, 2 as col2, 'Smith' as created_by FROM dual
UNION ALL SELECT 1 as col1, 2 as col2, 'John'  as created_by FROM dual
UNION ALL SELECT 1 as col1, 3 as col2, 'Ajay'  as created_by FROM dual
UNION ALL SELECT 1 as col1, 4 as col2, 'Ram'   as created_by FROM dual
UNION ALL SELECT 1 as col1, 5 as col2, 'Jack'  as created_by FROM dual
)
SELECT listagg(col1_with_nulls, ',') WITHIN GROUP (ORDER BY col1_with_nulls) col1_list
     , listagg(col2_with_nulls, ',') WITHIN GROUP (ORDER BY col2_with_nulls) col2_list
     , listagg(created_by, ',')      WITHIN GROUP (ORDER BY created_by) created_by_list
  FROM ( SELECT CASE WHEN lag(col1) OVER (ORDER BY col1) = col1 THEN NULL ELSE col1 END as col1_with_nulls
              , CASE WHEN lag(col2) OVER (ORDER BY col2) = col2 THEN NULL ELSE col2 END as col2_with_nulls
              , created_by
           FROM tab );

结果

COL1_LIST COL2_LIST CREATED_BY_LIST
--------- --------- -------------------------
1         2,3,4,5   Ajay,Jack,John,Ram,Smith

0

您可以通过RegEx替换来完成。这是一个例子:

-- Citations Per Year - Cited Publications main query. Includes list of unique associated core project numbers, ordered by core project number.
SELECT ptc.pmid AS pmid, ptc.pmc_id, ptc.pub_title AS pubtitle, ptc.author_list AS authorlist,
  ptc.pub_date AS pubdate,
  REGEXP_REPLACE( LISTAGG ( ppcc.admin_phs_org_code || 
    TO_CHAR(ppcc.serial_num,'FM000000'), ',') WITHIN GROUP (ORDER BY ppcc.admin_phs_org_code || 
    TO_CHAR(ppcc.serial_num,'FM000000')),
    '(^|,)(.+)(,\2)+', '\1\2')
  AS projectNum
FROM publication_total_citations ptc
  JOIN proj_paper_citation_counts ppcc
    ON ptc.pmid = ppcc.pmid
   AND ppcc.citation_year = 2013
  JOIN user_appls ua
    ON ppcc.admin_phs_org_code = ua.admin_phs_org_code
   AND ppcc.serial_num = ua.serial_num
   AND ua.login_id = 'EVANSF'
GROUP BY ptc.pmid, ptc.pmc_id, ptc.pub_title, ptc.author_list, ptc.pub_date
ORDER BY pmid;

还发布在这里:Oracle-唯一的Listagg值


0

使用如下创建的listagg_clob函数:

create or replace package list_const_p
is
list_sep varchar2(10) := ',';
end list_const_p;
/
sho err

create type listagg_clob_t as object(
v_liststring varchar2(32767),
v_clob clob,
v_templob number,

static function ODCIAggregateInitialize(
sctx IN OUT listagg_clob_t
) return number,
member function ODCIAggregateIterate(
self IN OUT listagg_clob_t, value IN varchar2
) return number,
member function ODCIAggregateTerminate(
self IN OUT listagg_clob_t, returnValue OUT clob, flags IN number
) return number,
member function ODCIAggregateMerge(
self IN OUT listagg_clob_t, ctx2 IN OUT listagg_clob_t
) return number
);
/
sho err

create or replace type body listagg_clob_t is

static function ODCIAggregateInitialize(sctx IN OUT listagg_clob_t)
return number is
begin
sctx := listagg_clob_t('', '', 0);
return ODCIConst.Success;
end;

member function ODCIAggregateIterate(
self IN OUT listagg_clob_t,
value IN varchar2
) return number is
begin
if nvl(lengthb(v_liststring),0) + nvl(lengthb(value),0) <= 4000 then
self.v_liststring:=self.v_liststring || value || list_const_p.list_sep;
else
if self.v_templob = 0 then
dbms_lob.createtemporary(self.v_clob, true, dbms_lob.call);
self.v_templob := 1;
end if;
dbms_lob.writeappend(self.v_clob, length(self.v_liststring), v_liststring);
self.v_liststring := value || list_const_p.list_sep;
end if;
return ODCIConst.Success;
end;

member function ODCIAggregateTerminate(
self IN OUT listagg_clob_t,
returnValue OUT clob,
flags IN number
) return number is
begin
if self.v_templob != 0 then
dbms_lob.writeappend(self.v_clob, length(self.v_liststring), self.v_liststring);
dbms_lob.trim(self.v_clob, dbms_lob.getlength(self.v_clob) - 1);
else
self.v_clob := substr(self.v_liststring, 1, length(self.v_liststring) - 1);
end if;
returnValue := self.v_clob;
return ODCIConst.Success;
end;

member function ODCIAggregateMerge(self IN OUT listagg_clob_t, ctx2 IN OUT listagg_clob_t) return number is
begin
if ctx2.v_templob != 0 then
if self.v_templob != 0 then
dbms_lob.append(self.v_clob, ctx2.v_clob);
dbms_lob.freetemporary(ctx2.v_clob);
ctx2.v_templob := 0;
else
self.v_clob := ctx2.v_clob;
self.v_templob := 1;
ctx2.v_clob := '';
ctx2.v_templob := 0;
end if;
end if;
if nvl(lengthb(self.v_liststring),0) + nvl(lengthb(ctx2.v_liststring),0) <= 4000 then
self.v_liststring := self.v_liststring || ctx2.v_liststring;
ctx2.v_liststring := '';
else
if self.v_templob = 0 then
dbms_lob.createtemporary(self.v_clob, true, dbms_lob.call);
self.v_templob := 1;
end if;
dbms_lob.writeappend(self.v_clob, length(self.v_liststring), self.v_liststring);
dbms_lob.writeappend(self.v_clob, length(ctx2.v_liststring), ctx2.v_liststring);
self.v_liststring := '';
ctx2.v_liststring := '';
end if;
return ODCIConst.Success;
end;
end;
/
sho err

CREATE or replace FUNCTION listagg_clob (input varchar2) RETURN clob
PARALLEL_ENABLE AGGREGATE USING listagg_clob_t;
/
sho err 


0

我写了一个函数来使用正则表达式来处理这个问题。in参数是:1)listagg自身调用2)重复定界符

create or replace function distinct_listagg
  (listagg_in varchar2,
   delimiter_in varchar2)

   return varchar2
   as
   hold_result varchar2(4000);
   begin

   select rtrim( regexp_replace( (listagg_in)
      , '([^'||delimiter_in||']*)('||
      delimiter_in||'\1)+($|'||delimiter_in||')', '\1\3'), ',')
      into hold_result
      from dual;

return hold_result;

end;

现在,您不必每次都重复正则表达式,只需说:

select distinct_listagg(
                       listagg(myfield,', ') within group (order by 1),
                       ', '
                       )
     from mytable;

0

如果不需要连接值的特定顺序,并且分隔符可以是逗号,则可以执行以下操作:

select col1, stragg(distinct col2)
  from table
 group by col1


0

一个令人讨厌的方面LISTAGG是,如果串联字符串的总长度超过4000个字符(VARCHAR2SQL中的限制),则会引发以下错误,这在Oracle 12.1版之前的版本中很难管理。

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

ON OVERFLOW子句是12cR2中新增的功能LISTAGG。包含此子句的查询如下所示:

SELECT pid, LISTAGG(Desc, ' ' on overflow truncate) WITHIN GROUP (ORDER BY seq) AS desc
FROM B GROUP BY pid;

以上将输出限制为4000个字符,但不会引发 ORA-01489错误。

这些是ON OVERFLOW子句的一些附加选项:

  • ON OVERFLOW TRUNCATE 'Contd..' :这将显示 'Contd..'在字符串的末尾(默认为...
  • ON OVERFLOW TRUNCATE '' :这将显示4000个字符,没有任何终止字符串。
  • ON OVERFLOW TRUNCATE WITH COUNT:这将在结束字符之后的末尾显示字符总数。例如:-'...(5512) '
  • ON OVERFLOW ERROR:如果您期望LISTAGG失败并显示 ORA-01489错误(始终为默认值)。

0

我实现了这个存储的功能:

CREATE TYPE LISTAGG_DISTINCT_PARAMS AS OBJECT (ELEMENTO VARCHAR2(2000), SEPARATORE VARCHAR2(10));

CREATE TYPE T_LISTA_ELEMENTI AS TABLE OF VARCHAR2(2000);

CREATE TYPE T_LISTAGG_DISTINCT AS OBJECT (

    LISTA_ELEMENTI T_LISTA_ELEMENTI,
        SEPARATORE VARCHAR2(10),

    STATIC FUNCTION ODCIAGGREGATEINITIALIZE(SCTX  IN OUT            T_LISTAGG_DISTINCT) 
                    RETURN NUMBER,

    MEMBER FUNCTION ODCIAGGREGATEITERATE   (SELF  IN OUT            T_LISTAGG_DISTINCT, 
                                            VALUE IN                    LISTAGG_DISTINCT_PARAMS ) 
                    RETURN NUMBER,

    MEMBER FUNCTION ODCIAGGREGATETERMINATE (SELF         IN     T_LISTAGG_DISTINCT,
                                            RETURN_VALUE OUT    VARCHAR2, 
                                            FLAGS        IN     NUMBER      )
                    RETURN NUMBER,

    MEMBER FUNCTION ODCIAGGREGATEMERGE       (SELF               IN OUT T_LISTAGG_DISTINCT,
                                                                                        CTX2                 IN         T_LISTAGG_DISTINCT    )
                    RETURN NUMBER
);

CREATE OR REPLACE TYPE BODY T_LISTAGG_DISTINCT IS 

    STATIC FUNCTION ODCIAGGREGATEINITIALIZE(SCTX IN OUT T_LISTAGG_DISTINCT) RETURN NUMBER IS 
    BEGIN
                SCTX := T_LISTAGG_DISTINCT(T_LISTA_ELEMENTI() , ',');
        RETURN ODCICONST.SUCCESS;
    END;

    MEMBER FUNCTION ODCIAGGREGATEITERATE(SELF IN OUT T_LISTAGG_DISTINCT, VALUE IN LISTAGG_DISTINCT_PARAMS) RETURN NUMBER IS
    BEGIN

                IF VALUE.ELEMENTO IS NOT NULL THEN
                        SELF.LISTA_ELEMENTI.EXTEND;
                        SELF.LISTA_ELEMENTI(SELF.LISTA_ELEMENTI.LAST) := TO_CHAR(VALUE.ELEMENTO);
                        SELF.LISTA_ELEMENTI:= SELF.LISTA_ELEMENTI MULTISET UNION DISTINCT SELF.LISTA_ELEMENTI;
                        SELF.SEPARATORE := VALUE.SEPARATORE;
                END IF;
        RETURN ODCICONST.SUCCESS;
    END;

    MEMBER FUNCTION ODCIAGGREGATETERMINATE(SELF IN T_LISTAGG_DISTINCT, RETURN_VALUE OUT VARCHAR2, FLAGS IN NUMBER) RETURN NUMBER IS
      STRINGA_OUTPUT            CLOB:='';
            LISTA_OUTPUT                T_LISTA_ELEMENTI;
            TERMINATORE                 VARCHAR2(3):='...';
            LUNGHEZZA_MAX           NUMBER:=4000;
    BEGIN

                IF SELF.LISTA_ELEMENTI.EXISTS(1) THEN -- se esiste almeno un elemento nella lista

                        -- inizializza una nuova lista di appoggio
                        LISTA_OUTPUT := T_LISTA_ELEMENTI();

                        -- riversamento dei soli elementi in DISTINCT
                        LISTA_OUTPUT := SELF.LISTA_ELEMENTI MULTISET UNION DISTINCT SELF.LISTA_ELEMENTI;

                        -- ordinamento degli elementi
                        SELECT CAST(MULTISET(SELECT * FROM TABLE(LISTA_OUTPUT) ORDER BY 1 ) AS T_LISTA_ELEMENTI ) INTO LISTA_OUTPUT FROM DUAL;

                        -- concatenazione in una stringa                        
                        FOR I IN LISTA_OUTPUT.FIRST .. LISTA_OUTPUT.LAST - 1
                        LOOP
                            STRINGA_OUTPUT := STRINGA_OUTPUT || LISTA_OUTPUT(I) || SELF.SEPARATORE;
                        END LOOP;
                        STRINGA_OUTPUT := STRINGA_OUTPUT || LISTA_OUTPUT(LISTA_OUTPUT.LAST);

                        -- se la stringa supera la dimensione massima impostata, tronca e termina con un terminatore
                        IF LENGTH(STRINGA_OUTPUT) > LUNGHEZZA_MAX THEN
                                    RETURN_VALUE := SUBSTR(STRINGA_OUTPUT, 0, LUNGHEZZA_MAX - LENGTH(TERMINATORE)) || TERMINATORE;
                        ELSE
                                    RETURN_VALUE:=STRINGA_OUTPUT;
                        END IF;

                ELSE -- se non esiste nessun elemento, restituisci NULL

                        RETURN_VALUE := NULL;

                END IF;

        RETURN ODCICONST.SUCCESS;
    END;

    MEMBER FUNCTION ODCIAGGREGATEMERGE(SELF IN OUT T_LISTAGG_DISTINCT, CTX2 IN T_LISTAGG_DISTINCT) RETURN NUMBER IS
    BEGIN
        RETURN ODCICONST.SUCCESS;
    END;

END; -- fine corpo

CREATE
FUNCTION LISTAGG_DISTINCT (INPUT LISTAGG_DISTINCT_PARAMS) RETURN VARCHAR2
    PARALLEL_ENABLE AGGREGATE USING T_LISTAGG_DISTINCT;

// Example
SELECT LISTAGG_DISTINCT(LISTAGG_DISTINCT_PARAMS(OWNER, ', ')) AS LISTA_OWNER
FROM SYS.ALL_OBJECTS;

抱歉,在某些情况下(对于很大的一组),Oracle可能会返回以下错误:

Object or Collection value was too large. The size of the value
might have exceeded 30k in a SORT context, or the size might be
too big for available memory.

但是我认为这是一个很好的起点;)


0

select col1, listaggr(col2,',') within group(Order by col2) from table group by col1 意思是将字符串(col2)聚合到列表中,保持顺序n,然后按col1分组处理重复项,这意味着将col1重复项合并为1组。也许这看起来应该看起来很干净和简单,如果还需要col3,则只需要再添加一个listagg()select col1, listaggr(col2,',') within group(Order by col2),listaggr(col3,',') within group(order by col3) from table group by col1


0

使用 SELECT DISTINCT ...@a_horse_with_no_name指出,在调用LISTAGG之前,用作子查询的一部分可能是进行简单查询的最佳方法。

但是,在更复杂的查询中,可能无法或不容易做到这一点。我在使用使用解析函数的top-n方法的情况下遇到了这种情况。

所以我找到了COLLECT聚合函数。它被记录为具有UNIQUEDISTINCT修饰符。仅在10g中,它就会安静地失败(它会忽略修饰符而不会出错)。但是,为了克服这个问题,从另一个答案中,我得出了以下解决方案:

SELECT
  ...
  (
    SELECT LISTAGG(v.column_value,',') WITHIN GROUP (ORDER BY v.column_value)
    FROM TABLE(columns_tab) v
  ) AS columns,
  ...
FROM (
  SELECT
    ...
    SET(CAST(COLLECT(UNIQUE some_column ORDER BY some_column) AS tab_typ)) AS columns_tab,
    ...
)

基本上,通过使用SET,可以删除集合中的重复项。

您仍然需要将定义tab_typ为基本集合类型,在的情况下,VARCHAR例如:

CREATE OR REPLACE type tab_typ as table of varchar2(100)
/

另外,作为多列情况下@a_horse_with_no_name的答案的更正,您可能希望在第三(或更多)列上进行聚合:

select
  col1, 
  listagg(CASE rn2 WHEN 1 THEN col2 END, ',') within group (order by col2) AS col2_list,
  listagg(CASE rn3 WHEN 1 THEN col3 END, ',') within group (order by col3) AS col3_list,
  SUM(col4) AS col4
from (
  select
    col1, 
    col2,
    row_number() over (partition by col1, col2 order by null) as rn2,
    row_number() over (partition by col1, col3 order by null) as rn3
  from foo
)
group by col1;

如果将查询保留rn = 1为where条件,则会错误地聚合其他列。


0

非常简单-在查询中使用具有不同选择的子查询:

SELECT question_id,
       LISTAGG(element_id, ',') WITHIN GROUP (ORDER BY element_id)
FROM
       (SELECT distinct question_id, element_id
       FROM YOUR_TABLE)
GROUP BY question_id;

-1

处理多个listagg的最简单方法是,每列包含1个WITH(子查询因子),其中包含该列的listagg(来自不同的选择对象):

    WITH tab AS 
    (           
        SELECT 1 as col1, 2 as col2, 3 as col3, 'Smith' as created_by FROM dual
        UNION ALL SELECT 1 as col1, 2 as col2, 3 as col3,'John'  as created_by FROM dual
        UNION ALL SELECT 1 as col1, 3 as col2, 4 as col3,'Ajay'  as created_by FROM dual
        UNION ALL SELECT 1 as col1, 4 as col2, 4 as col3,'Ram'   as created_by FROM dual
        UNION ALL SELECT 1 as col1, 5 as col2, 6 as col3,'Jack'  as created_by FROM dual
    )
    , getCol2 AS
    (
        SELECT  DISTINCT col1, listagg(col2,',') within group (order by col2)  over (partition by col1) AS col2List
        FROM ( SELECT DISTINCT col1,col2 FROM tab)
    )
    , getCol3 AS
    (
        SELECT  DISTINCT col1, listagg(col3,',') within group (order by col3)  over (partition by col1) AS col3List
        FROM ( SELECT DISTINCT col1,col3 FROM tab)
    )
    select col1,col2List,col3List
    FROM getCol2
    JOIN getCol3
    using (col1)

这使:

col1  col2List  col3List
1     2,3,4,5   3,4,6
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.