Oracle distinct listagg 与计数

Posted

技术标签:

【中文标题】Oracle distinct listagg 与计数【英文标题】:Oracle distinct listagg with count 【发布时间】:2017-11-14 20:01:43 【问题描述】:

我需要为每个表格行显示一个逗号分隔的描述,但我需要列表是不同的,并且对一些可用的描述有计数。

every_description     needs_count
---------------------------------
Bred                          yes
From Vendor                   yes
Grouped                        no
Removed                       yes
Separated                      no
Weaned                        yes

所以在一天之内,描述可能类似于 Bred, Grouped, Weaned,我现在使用 LISTAGG 并使用提到的解决方案 here 删除重复项,但我需要为一些描述添加计数,例如5 Bred, Grouped, 2 Weaned.

这是我当前遇到的问题:

WITH cages AS (
        SELECT 1234 AS id FROM DUAL
  UNION SELECT 5678 AS id FROM DUAL
  UNION SELECT 9012 AS id FROM DUAL
  UNION SELECT 3456 AS id FROM DUAL
), cage_comments AS (
        SELECT 1234 AS cage_id, 'Bred' AS description, TO_DATE('11/14/2017', 'MM/DD/YYYY') AS event_date FROM DUAL
  UNION SELECT 5678 AS cage_id, 'Grouped' AS description, TO_DATE('11/14/2017', 'MM/DD/YYYY') AS event_date FROM DUAL
  UNION SELECT 9012 AS cage_id, 'Weaned' AS description, TO_DATE('11/14/2017', 'MM/DD/YYYY') AS event_date FROM DUAL
  UNION SELECT 3456 AS cage_id, 'Weaned' AS description, TO_DATE('11/14/2017', 'MM/DD/YYYY') AS event_date FROM DUAL
  UNION SELECT 3456 AS cage_id, 'Bred' AS description, TO_DATE('11/02/2017', 'MM/DD/YYYY') AS event_date FROM DUAL
  UNION SELECT 3456 AS cage_id, 'Grouped' AS description, TO_DATE('11/14/2017', 'MM/DD/YYYY') AS event_date FROM DUAL
), calendar AS (
  SELECT dt
  FROM (
    SELECT TRUNC(LAST_DAY(TO_DATE(&month || '/01/' || &year, 'MM/DD/YYYY')) - ROWNUM + 1) dt
    FROM DUAL CONNECT BY ROWNUM <= 31
  )
  WHERE dt >= TRUNC(TO_DATE(&month || '/01/' || &year, 'MM/DD/YYYY'), 'MM')
  ORDER BY dt ASC
)

SELECT
  cal.dt,
  (
    SELECT
      CASE
        WHEN COUNT(cc.cage_id) > 0 THEN RTRIM(
          REGEXP_REPLACE(
            (LISTAGG(cc.description, ',') WITHIN GROUP (ORDER BY cc.description)),
            '([^,]*)(,\1)+($|,)',
            '\1\3'
          ),
          ','
        )
        ELSE NULL
      END
    FROM cages c
    LEFT JOIN cage_comments cc ON cc.cage_id = c.id
    WHERE cc.event_date = cal.dt
  ) AS description
FROM calendar cal
ORDER BY cal.dt

简而言之 - 我只是在为当天的某些描述添加 COUNT 时遇到了困难。在上述情况下,我想说1 Bred 代表November 2, 20171 Bred, Grouped, 2 Weaned 代表November 14, 2017

【问题讨论】:

请发布 CREATE TABLE 和 INSERT INTO 以重新创建您的案例 @lad2025 我添加了一个测试用例。应该是即插即用的,只需对日历参数执行112017 【参考方案1】:

目前,您正在汇总所有描述(没有 DISTINCT),然后使用正则表达式替换删除重复的描述。这是非常低效的 - 最好选择 distinct 然后应用 LISTAGG。

如果您需要添加计数,这将变得更加重要。获取您的加入结果和 GROUP BY 描述。 (特别是,这将照顾 DISTINCT)。在此聚合步骤的 SELECT 中,包括计数。然后将结果连接到问题顶部的附加表中,并将参数重新写入 LISTAGG 以包含一个 CASE 表达式,等于 needs_count 值为 'yes' 时的计数(和一个空格)。

【讨论】:

可爱。感谢您的解决方案!【参考方案2】:

你可以使用:

SELECT
  cal.dt,
  ( 
      SELECT LISTAGG(CASE WHEN COUNT(*)=1 THEN '' 
            ELSE CAST(COUNT(*) AS VARCHAR2(10)) || ' ' END  || description, ', ')
            WITHIN GROUP (ORDER BY description) 
      FROM cages c
      LEFT JOIN cage_comments cc ON cc.cage_id = c.id
      WHERE cc.event_date = cal.dt
      GROUP BY cc.event_date, cc.description
  ) AS description
FROM calendar cal
ORDER BY cal.dt;

DBFiddle Demo

【讨论】:

这很接近 - 只需稍微修改一下 (DBFiddle)。

以上是关于Oracle distinct listagg 与计数的主要内容,如果未能解决你的问题,请参考以下文章

SQLSERVER 中的 ListAGG

在红移中具有 DISTINCT 的 listagg

H2 用户定义的聚合函数 ListAgg 不能在第一个参数上使用 DISTINCT 或 TRIM()

DB2-具有DISTINCT子句的LISTAGG()-不起作用?

oracle12 listagg 与 wm_concat行列转换

oracle 12c 关于wm_concat 的替换;LISTAGG