前几天,有一个业务,这个业务是统计每个月的累计用户数量,用于做大屏展示使用。
为了省去前端的开发时间,大屏展示使用了开源工具davinci,该工具集成了echarts报表,可以通过编写SQL,选择图表类型,拖动和缩放图表生成自己的报表或大屏页面。
但是这样就导致了不能在查询出结果后,不能对数据进行二次加工;另外我们本次连的是业务库,我们只有查询权限,这就封禁了建中间表的方式。所以只能在SQL上下功夫了。
以下是我所有sql的迭代过程。
t_user用户表简化模型如下:
user_name | user_type | create_month |
---|---|---|
张一 | 1 | 2020-05 |
张二 | 1 | 2020-05 |
张三 | 1 | 2020-04 |
张四 | 1 | 2020-03 |
李一 | 2 | 2020-05 |
李二 | 2 | 2020-04 |
李三 | 2 | 2020-02 |
使用如下sql统计每个月,每个类型的用户新增人数。
SELECT
user_type,
create_month,
count( 1 ) AS cnt
FROM
t_user
GROUP BY
user_type,
create_month
查询结果如下
user_type | create_month | cnt |
---|---|---|
1 | 2020-03 | 1 |
1 | 2020-04 | 1 |
1 | 2020-05 | 2 |
2 | 2020-02 | 1 |
2 | 2020-04 | 1 |
2 | 2020-05 | 1 |
使用如下sql统计每个月,每个类型的用户累计用户数。
SELECT
user_type,
create_month,
(
SELECT
sum( cnt )
FROM
( SELECT user_type, create_month, count( 1 ) cnt
FROM t_user GROUP BY user_type, create_month ) b
WHERE
b.create_month <= a.create_month
AND b.user_type = a.user_type
) AS total
FROM
t_user a
GROUP BY
user_type,
create_month
查询结果如下
user_type | create_month | total |
---|---|---|
1 | 2020-03 | 1 |
1 | 2020-04 | 2 |
1 | 2020-05 | 4 |
2 | 2020-02 | 1 |
2 | 2020-04 | 2 |
2 | 2020-05 | 3 |
在案例 3 基础上,汇总每个月的 1 类和 2 类用户为 3 。
SELECT
user_type,
create_month,
(
SELECT
sum( cnt )
FROM
( SELECT user_type, create_month, count( 1 ) cnt
FROM t_user GROUP BY user_type, create_month ) b
WHERE
b.create_month <= a.create_month
AND b.user_type = a.user_type
) AS total
FROM
t_user a
GROUP BY
user_type,
create_month UNION ALL
SELECT
3 AS user_type,
create_month,
(
SELECT
sum( cnt )
FROM
( SELECT create_month, count( 1 ) cnt FROM t_user GROUP BY create_month ) b
WHERE
b.create_month <= a.create_month
) AS total
FROM
t_user a
GROUP BY
create_month
查询结果如下
user_type | create_month | total |
---|---|---|
1 | 2020-03 | 1 |
1 | 2020-04 | 2 |
1 | 2020-05 | 4 |
2 | 2020-02 | 1 |
2 | 2020-04 | 2 |
2 | 2020-05 | 3 |
3 | 2020-02 | 1 |
3 | 2020-03 | 2 |
3 | 2020-04 | 4 |
3 | 2020-05 | 7 |
在案例 3 的基础上,把分类 1 和 2 的缺失月份的数据补全为 0。
思路为:先把分类和月份做全连接,这样就可以得出分类和月的所有排列组合;然后把数量对应到所在的分类和月份,没有数量的使用 ifnull(total, 0) 补全为 0。
SELECT
b.user_type,
b.create_month,
ifnull( a.total, 0 ) AS total
FROM
(
SELECT
user_type,
create_month,
(
SELECT
sum( cnt )
FROM
( SELECT user_type, create_month, count( 1 ) cnt
FROM t_user GROUP BY user_type, create_month ) b
WHERE
b.create_month <= a.create_month
AND b.user_type = a.user_type
) AS total
FROM
t_user a
GROUP BY
user_type,
create_month
) a
RIGHT JOIN (
SELECT
*
FROM
( SELECT user_type FROM t_user GROUP BY user_type ) a
LEFT JOIN ( SELECT create_month FROM t_user GROUP BY create_month ) b ON 1 = 1
) b ON a.user_type = b.user_type
AND a.create_month = b.create_month
ORDER BY
b.user_type,
b.create_month
查询结果如下
user_type | create_month | total |
---|---|---|
1 | 2020-02 | 0 |
1 | 2020-03 | 1 |
1 | 2020-04 | 2 |
1 | 2020-05 | 4 |
2 | 2020-02 | 1 |
2 | 2020-03 | 0 |
2 | 2020-04 | 2 |
2 | 2020-05 | 3 |
把 4 和 5 结合起来,就可以兼顾补全缺失的月份和添加汇总分类。
SELECT
b.user_type,
b.create_month,
ifnull( a.total, 0 ) AS total
FROM
(
SELECT
user_type,
create_month,
(
SELECT
sum( cnt )
FROM
( SELECT user_type, create_month, count( 1 ) cnt
FROM t_user GROUP BY user_type, create_month ) b
WHERE
b.create_month <= a.create_month
AND b.user_type = a.user_type
) AS total
FROM
t_user a
GROUP BY
user_type,
create_month
) a
RIGHT JOIN (
SELECT
*
FROM
( SELECT user_type FROM t_user GROUP BY user_type ) a
LEFT JOIN ( SELECT create_month FROM t_user GROUP BY create_month ) b ON 1 = 1
) b ON a.user_type = b.user_type
AND a.create_month = b.create_month UNION ALL
SELECT
3 AS user_type,
create_month,
(
SELECT
sum( cnt )
FROM
( SELECT create_month, count( 1 ) cnt FROM t_user GROUP BY create_month ) b
WHERE
b.create_month <= a.create_month
) AS total
FROM
t_user a
GROUP BY
create_month
ORDER BY
user_type,
create_month
查询结果如下
user_type | create_month | total |
---|---|---|
1 | 2020-02 | 0 |
1 | 2020-03 | 1 |
1 | 2020-04 | 2 |
1 | 2020-05 | 4 |
2 | 2020-02 | 1 |
2 | 2020-03 | 0 |
2 | 2020-04 | 2 |
2 | 2020-05 | 3 |
3 | 2020-02 | 1 |
3 | 2020-03 | 2 |
3 | 2020-04 | 4 |
3 | 2020-05 | 7 |
任何一个复杂需求都可以拆分成简单的需求。
就像这个需求,如果一开始就让我知道最终的SQL会这么复杂,那我肯定绕道了。
吼吼~~