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

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

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

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

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

1、数据库简化模型

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

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

使用如下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

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

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

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

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

7、总结

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

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

吼吼~~

你可能感兴趣的:(mysql,mysql)