怎样把一张三个字段的表统计出花来:SQL在分类或按时间统计时,补全缺失的分类或时间

前几天,有一个业务,这个业务是统计每个月的累计用户数量,用于做大屏展示使用。

为了省去前端的开发时间,大屏展示使用了开源工具davinci,该工具集成了echarts报表,可以通过编写SQL,选择图表类型,拖动和缩放图表生成自己的报表或大屏页面。

但是这样就导致了不能在查询出结果后,不能对数据进行二次加工;另外我们本次连的是业务库,我们只有查询权限,这就封禁了建中间表的方式。所以只能在SQL上下功夫了。

以下是我所有sql的迭代过程。

1、数据库简化模型

t_user用户表简化模型如下:

user_nameuser_typecreate_month
张一12020-05
张二12020-05
张三12020-04
张四12020-03
李一22020-05
李二22020-04
李三22020-02

2、按类型分组查询每月新增用户

使用如下sql统计每个月,每个类型的用户新增人数。

SELECT
	user_type,
	create_month,
	count( 1 ) AS cnt 
FROM
	t_user 
GROUP BY
	user_type,
	create_month

查询结果如下

user_typecreate_monthcnt
12020-031
12020-041
12020-052
22020-021
22020-041
22020-051

3、初级进阶:按类型分组查询每个月累计用户

使用如下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_typecreate_monthtotal
12020-031
12020-042
12020-054
22020-021
22020-042
22020-053

4、中级进阶:在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_typecreate_monthtotal
12020-031
12020-042
12020-054
22020-021
22020-042
22020-053
32020-021
32020-032
32020-044
32020-057

5、高级进阶:在3的基础上补全缺失的月份

在案例 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_typecreate_monthtotal
12020-020
12020-031
12020-042
12020-054
22020-021
22020-030
22020-042
22020-053

6、变态进阶:在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_typecreate_monthtotal
12020-020
12020-031
12020-042
12020-054
22020-021
22020-030
22020-042
22020-053
32020-021
32020-032
32020-044
32020-057

7、总结

任何一个复杂需求都可以拆分成简单的需求。

就像这个需求,如果一开始就让我知道最终的SQL会这么复杂,那我肯定绕道了。

🐒吼吼~~

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值