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

MySQL query to count cumulative user number based on activation date groupped by month

我有以下 table 和数据:

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 中处理它吗? 谢谢

如果你是 运行 MySQL 8.0,你可以使用递归查询来生成月份,然后将 table 带上 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

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

Demo on DB Fiddle:

dt         | cumulative
:--------- | ---------:
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

从 MySQL 8.0 开始,您可以使用 window 函数来完成此任务:

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 |
+-------+------------+