如何使用 MySQL 表示分区上的组计数

Posted

技术标签:

【中文标题】如何使用 MySQL 表示分区上的组计数【英文标题】:How to represent group counts over a partition using MySQL 【发布时间】:2021-07-01 18:12:32 【问题描述】:

一段时间以来一直在摸索这个问题,所以我的数据看起来像这样:

   USER  GROUP        DATE
    001     AA    02-20-21
    007     AA    02-20-21
    002     DD    02-20-21
    003     DD    02-21-21
    004     BB    02-21-21
    018     BB    02-22-21
    005     AA    02-22-21
    006     EE    02-22-21
    022     AA    02-22-21

就目前而言,我将这些数据汇总如下:

DATE      GROUP   USER_COUNT   NEW_USER_COUNT
02-20-21     AA            2                2
02-20-21     DD            1                1
02-21-21     DD            2                1
02-21-21     BB            1                1
02-22-21     BB            2                1
02-22-21     AA            4                2
02-22-21     EE            1                1

这样做的目的是让我获得每个单独组的用户计数,以及从组的最后一次迭代到下一次迭代的新用户计数。

现在这可以正常工作,但是有一个关键问题。出于可视化目的,我需要在每个日期中表示每个组。就目前而言,如果一个组没有出现在某个日期,它就不会显示;我需要每个组出现在每个日期,所以如果我们在给定日期没有用户,我们会使用以前的 user_count 没有新用户。

这就是我希望上述数据的外观:

DATE      GROUP   USER_COUNT   NEW_USER_COUNT
02-20-21     AA            2                2
02-20-21     DD            1                1
02-21-21     AA            2                0
02-21-21     DD            2                1
02-21-21     BB            1                1
02-22-21     AA            4                2
02-22-21     DD            2                0
02-22-21     BB            2                1
02-22-21     EE            1                1

请注意,在 AA 和 DD 出现之后,它们会继续出现在每个 DATE 上,即使它们没有增长,它们也会使用最后一个数字。

因此,基本上,在第一次出现新组后,它会在以后的每个日期出现。新组不固定,随时可能出现新组。

这是我现有的查询:

WITH NEW_USER AS (
    SELECT USER,
        DATE,
        GROUP,
        ROW_NUMBER() OVER (
            PARTITION BY USER
            ORDER BY DATE
        ) AS row_n
    FROM dt
),
/*Increment count of unique users based on GROUP. */
CUMULATIVE_USER_COUNT AS (
    SELECT DATE,
        GROUP,
        SUM(COUNT(*)) OVER (
            PARTITION BY GROUP
            ORDER BY DATE
        ) AS USER_COUNT,
        COUNT(DISTINCT USER) AS NEW_USER_COUNT
    FROM NEW_USER
    WHERE row_n = 1
    GROUP BY DATE,
        GROUP
    ORDER BY DATE,
        GROUP
)
SELECT *
FROM CUMULATIVE_USER_COUNT

另外仅供参考,每行的 USER ID 不是唯一的,DISTINCT USER 是必需的。

【问题讨论】:

我还是不明白你是如何构造 NEW_USER_COUNT @nbk NEW_USER_COUNT 基本上是每天 USER_COUNT 组的差异。因此,当 DD 第一次出现在 2/20/21 时,它是 1,因为 1+0 = 1(用户计数)。然后当 DD 在 2/21/21 再次出现时,我们得到 2 表示 USER_COUNT 和 1 表示 NEW_USER_COUNT。 (2/20/21 来自 USER_COUNT 的 1 个 + 1 = 2/21/21 的 2 个 USER_COUNT。有意义吗? 请澄清您的示例数据 - 02-20-21mysql 中不是合法的 DATE 值。 更复杂的是必须添加缺少的日期,这使得查询更加复杂,此外我必须使用 ST_TO_DATE 【参考方案1】:
WITH
cte1 AS ( SELECT DISTINCT `DATE`
          FROM test ),
cte2 AS ( SELECT DISTINCT `GROUP`
          FROM test ),
cte3 AS ( SELECT `DATE`, 
                 `GROUP`,
                 SUM(COUNT(test.USER)) OVER (PARTITION BY `GROUP` ORDER BY `DATE`) USER_COUNT,
                 COUNT(test.USER) NEW_USER_COUNT
          FROM cte1
          CROSS JOIN cte2
          LEFT JOIN test USING (`DATE`, `GROUP`)
          GROUP BY `DATE`, `GROUP` )
SELECT *
FROM cte3
WHERE USER_COUNT 
ORDER BY `DATE`, `GROUP` 

https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=bb4c55ac8897c5f5dcc84c1267f080c0

【讨论】:

【参考方案2】:

您可以使用cross join 生成行——使用一个扭曲来处理日期。然后引入现有数据:

select d.date, g.group,
       count(dt.date) as num_on_day,
       sum(count(dt.date)) over (partition by g.group order by d.date) as running_num
from (select distinct date
      from dt
     ) d join
     (select group, min(date) as min_date
      from dt
      group by group
     ) g
     on d.date >= g.min_date left join
     (select dt.*,
             row_number() over (partition by group, user_id order by date) as seqnum
      from dt
     ) dt
     on dt.date = d.date and dt.group = g.group and dt.seqnum = 1
group by d.date, g.group;

【讨论】:

以上是关于如何使用 MySQL 表示分区上的组计数的主要内容,如果未能解决你的问题,请参考以下文章

如何从MYSQL中的组中查找前N个记录的查询结果

如何避免mysql的double自动转为科学计数法表示

如何在 Oracle 中使用分区获取记录计数

如何避免mysql的double自动转为科学计数法表示

计数窗口函数 MySQL 中每个分区的最大计数

如何根据使用 dplyr 的组上的聚合函数计算新列(在汇总统计信息上添加汇总统计信息)?