MySQL查询根据按月分组的激活日期计算累积用户数

Posted

技术标签:

【中文标题】MySQL查询根据按月分组的激活日期计算累积用户数【英文标题】:MySQL query to count cumulative user number based on activation date groupped by month 【发布时间】:2020-10-20 20:14:22 【问题描述】:

我有以下表格和数据:

CREATE TABLE tbl_users (
  `id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
  `activation_date` DATETIME
  );

INSERT INTO tbl_users (id, activation_date) VALUES
    (1, '2020-01-15' ),
    (2, '2020-02-13' ),
    (3, '2020-02-15' ),
    (4, '2020-03-01' ),
    (5, '2020-03-03' ),
    (6, '2020-05-01' ),
    (7, '2020-06-01' ),
    (8, '2020-07-15' ),
    (9, '2020-08-15' ),
    (10, '2020-08-15' ),
    (11, '2020-08-19' );

我正在寻找基于激活日期在每个月底计算用户汇总数的最佳方法。上面输出的测试数据应该如下所示:

month   cumulative
1       1
2       3
3       5
4       5
5       6
6       7
7       8
8       11
9       11
10      11

我正在尝试:

SELECT MONTH(activation_date) as month, COUNT(*) as cumulative 
FROM tbl_users 
WHERE activation_date >= :start GROUP BY month

但我得到的是特定月份的值而不是累积值。 知道如何改进查询吗? 还是我需要稍后在 php 中处理它? 谢谢。

【问题讨论】:

您是在寻找“最佳方式”还是只是“一种方式”? 【参考方案1】:

mysql 8.0 开始,您可以使用窗口函数来完成此任务:

SELECT DISTINCT
    MONTH(activation_date) as month, 
    SUM(1) over (order by MONTH(activation_date) )as cumulative 
FROM tbl_users 
WHERE activation_date >= :start 
;

SQLize.online

结果:

+-------+------------+
| month | cumulative |
+-------+------------+
|     1 |          1 |
|     2 |          3 |
|     3 |          5 |
|     5 |          6 |
|     6 |          7 |
|     7 |          8 |
|     8 |         11 |
+-------+------------+

【讨论】:

当没有用户被激活时,这几个月都无法生成一行,例如四月。【参考方案2】:

如果您运行的是 MySQL 8.0,您可以使用递归查询来生成月份,然后将表带上 left join,最后计算累积和:

with recursive cte as (
    select 
        date_format(min(activation_date), '%Y-%m-01') dt,
        date_format(max(activation_date), '%Y-%m-01') max_dt
    from tbl_users
    union all
    select dt + interval 1 month, max_dt
    from cte
    where dt < max_dt
)
select c.dt, sum(count(u.id)) over(order by dt) cumulative
from cte c
left join tbl_users u
    on  u.activation_date >= c.dt
    and u.activation_date <  c.dt + interval 1 month
group by c.dt
order by c.dt

请注意,这会直接从表格中的可用日期生成日期范围的下限和上限,这似乎比使用固定范围更明智。

Demo on DB Fiddle

dt |累积 :--------- | ---------: 2020-01-01 | 1 2020-02-01 | 3 2020-03-01 | 5 2020-04-01 | 5 2020-05-01 | 6 2020-06-01 | 7 2020-07-01 | 8 2020-08-01 | 11

【讨论】:

只是一个务实的问题:对于大型表(几 k 行)并且通常是查询,是否建议使查询复杂化,或者仅针对月份值并在将数据检索到PHP数组?这种递归操作对数据库的影响会很大吗?谢谢!

以上是关于MySQL查询根据按月分组的激活日期计算累积用户数的主要内容,如果未能解决你的问题,请参考以下文章

mysql 如何按月分组查询出当前年度每个月的短信数量(数据库中这个月要是为空的话就用0条怎么显示出来)

SQL 查询:计数,按月-年分组,具有多个日期字段

SQL按月统计,按日分组

如何简化 Laravel 查询以按年搜索日期并按月分组

按月分组的Mysql查询

按日期分组的 MySQL 累积总和