消除 ListAgg (Oracle) 中的重复项

Lei*_*fel 48 oracle aggregate oracle-11g-r2

在 Oracle 11.2 之前,我使用自定义聚合函数将一列连接成一行。11.2 增加了这个LISTAGG功能,所以我想改用它。我的问题是我需要消除结果中的重复项,但似乎无法做到这一点。

这是一个例子。

CREATE TABLE ListAggTest AS (
  SELECT rownum Num1, DECODE(rownum,1,'2',to_char(rownum)) Num2 FROM dual 
     CONNECT BY rownum<=6
  );
SELECT * FROM ListAggTest;
Run Code Online (Sandbox Code Playgroud)
CREATE TABLE ListAggTest AS (
  SELECT rownum Num1, DECODE(rownum,1,'2',to_char(rownum)) Num2 FROM dual 
     CONNECT BY rownum<=6
  );
SELECT * FROM ListAggTest;
Run Code Online (Sandbox Code Playgroud)

我想看到的是:

      NUM1 NUM2
---------- ---------------------
         1 2
         2 2                    << Duplicate 2
         3 3
         4 4
         5 5
         6 6
Run Code Online (Sandbox Code Playgroud)

这是一个listagg接近但不消除重复的版本。

SELECT Num1, listagg(Num2,'-') WITHIN GROUP (ORDER BY NULL) OVER () Num2s 
FROM ListAggTest;
Run Code Online (Sandbox Code Playgroud)

我有一个解决方案,但它比继续使用自定义聚合函数更糟糕。

Jac*_*las 37

您可以使用正则表达式并regexp_replace在与 连接后删除重复项listagg

SELECT Num1, 
       RTRIM(
         REGEXP_REPLACE(
           (listagg(Num2,'-') WITHIN GROUP (ORDER BY Num2) OVER ()), 
           '([^-]*)(-\1)+($|-)', 
           '\1\3'),
         '-') Num2s 
FROM ListAggTest;
Run Code Online (Sandbox Code Playgroud)

如果 Oracle 的 regex 风格支持前瞻或非捕获组,这可能会更整洁,但它不支持

但是,此解决方案确实避免了多次扫描源。

DBFiddle在这里

  • 这就是`ORDER BY Num2` 实现的不是吗(参见[这里](http://dbfiddle.uk/?rdbms=oracle_11.2&amp;fiddle=9936641bbd880912254ec0db2ef88185))。或者您只是想指出您需要 ORDER BY 才能使其工作? (2认同)

Ren*_*ger 13

据我所知,使用当前可用的语言规范,如果必须使用listagg.

select distinct
       a.Num1, 
       b.num2s
  from listaggtest a cross join (
       select listagg(num2d, '-') within group (order by num2d) num2s 
       from (
         select distinct Num2 num2d from listaggtest
       )
      ) b;
Run Code Online (Sandbox Code Playgroud)

您的哪个解决方案比自定义聚合解决方案更糟糕?


小智 11

尽管这是一篇已接受答案的旧帖子,但我认为 LAG() 分析函数在这种情况下运行良好,并且值得注意:

  • LAG() 以最小的费用删除列 num2 中的重复值
  • 不需要非平凡的正则表达式来过滤结果
  • 只需一次全表扫描(简单示例表上的成本= 4)

这是建议的代码:

with nums as (
SELECT 
    num1, 
    num2, 
    decode( lag(num2) over (partition by null order by num2), --get last num2, if any
            --if last num2 is same as this num2, then make it null
            num2, null, 
            num2) newnum2
  FROM ListAggTest
) 
select 
  num1, 
  --listagg ignores NULL values, so duplicates are ignored
  listagg( newnum2,'-') WITHIN GROUP (ORDER BY Num2) OVER () num2s
  from nums;
Run Code Online (Sandbox Code Playgroud)

下面的结果似乎是 OP 想要的:

NUM1  NUM2S       
1   2-3-4-5-6
2   2-3-4-5-6
3   2-3-4-5-6
4   2-3-4-5-6
5   2-3-4-5-6
6   2-3-4-5-6 
Run Code Online (Sandbox Code Playgroud)


Lei*_*fel 8

创建一个自定义聚合函数来执行此操作。

Oracle 数据库提供了许多预定义的聚合函数,例如 MAX、MIN、SUM,用于对一组记录执行操作。这些预定义的聚合函数只能用于标量数据。但是,您可以创建这些函数的自定义实现,或定义全新的聚合函数,以用于复杂数据,例如,使用对象类型、不透明类型和 LOB 存储的多媒体数据。

用户定义的聚合函数在 SQL DML 语句中使用,就像 Oracle 数据库内置聚合一样。一旦向服务器注册了此类函数,数据库就会调用您提供的聚合例程而不是本机的聚合例程。

用户定义的聚合也可用于标量数据。例如,为处理与金融或科学应用相关的复杂统计数据而实施特殊的聚合函数可能是值得的。

用户定义的聚合是可扩展性框架的一项功能。您可以使用 ODCIAggregate 接口例程来实现它们。


Lei*_*fel 7

这是我对这个问题的解决方案,在我看来,它不如使用我们已经存在的自定义聚合函数好。

SELECT Num1, listagg(Num2,'-') WITHIN GROUP (ORDER BY NULL) OVER () Num2s FROM (
  SELECT Num1, DECODE(ROW_NUMBER() OVER (PARTITION BY Num2 ORDER BY NULL),
     1,Num2,NULL) Num2 FROM ListAggTest
);
Run Code Online (Sandbox Code Playgroud)


小智 5

您还可以使用 collect 语句,然后编写一个自定义的 pl/sql 函数,将集合转换为字符串。

CREATE TYPE varchar2_ntt AS TABLE OF VARCHAR2(4000);
CREATE TYPE varchar2_ntt AS TABLE OF VARCHAR2(4000);

select cast(collect(distinct num2 order by num2) as varchar2_ntt) 
from listaggtest
Run Code Online (Sandbox Code Playgroud)

您可以在子句中使用distinctand order bycollect但如果结合使用distinct,则从 11.2.0.2 起将不起作用:(

解决方法可能是一个子选择:

select collect(num2 order by num2) 
from 
( 
    select distinct num2 
    from listaggtest
)
Run Code Online (Sandbox Code Playgroud)


小智 5

请改用 WMSYS.WM_Concat。

SELECT Num1, Replace(Wm_Concat(DISTINCT Num2) OVER (), ',', '-')
FROM ListAggTest;
Run Code Online (Sandbox Code Playgroud)

注意:此功能未记录且不受支持。请参阅https://forums.oracle.com/forums/message.jspa?messageID=4372641#4372641

  • 如果您致电 Oracle 支持并且您正在使用 `wm_concat`(即使您认为 `wm_concat` 本身并不是导致问题的原因)[他们有理由拒绝提供帮助](https://forums.oracle.com/ forums/message.jspa?messageID=4372641#4372641) 因为它没有记录且不受支持 - 如果您使用自定义聚合或任何其他受支持的功能,则情况并非如此。 (8认同)