MySql 根据日期 group by 统计数据并将时间不存在的记录补全(纯 sql ,不建表)

开始

最近同事需要一个根据时间聚合统计数据的查询结果集,并且最好是可以把没有记录的日期也可以补全,我搜索了一下只找到说建立一个日期表然后联表查询方法,但是多一张没用的表,太不优雅了,所以我打算自己试试写一条sql尝试将结果查询出来。

思考

由于需要连续的日期,我们想到可以通过MySQL的DATE_ADD或DATE_SUB来获得。那么函数中的type固定用DAY,但是expr我们怎么生成呢?这时候我们就可以设置一个变量不断递增来获取我们需要天数的日期,这样连续的日期就完成了,然后我们联合根据时间 group by 查询出来的结果集查询即可。

实践

有一张 user 表,有 user_id 和 create_time 两个字段,模拟查询每天注册人数,暂定查询今天起往前20天(示例简单为主,具体需求可各自扩展)

  1. 根据时间聚合查出结果集
SELECT
	DATE( u.create_time ) AS create_time,
	count( u.user_id ) AS total
FROM
	`user` u 
GROUP BY
	DATE( u.create_time )
  1. 连续时间查询的结果集
SET @i :=- 1;
SELECT
	date_format( DATE_SUB( NOW( ), INTERVAL ( @i := @i + 1 ) DAY ), '%Y-%m-%d' ) AS `time` 
FROM
	`user` 
WHERE
	@i <= 20
  1. 联合 1 和 2 两个结果集的数据
SET @i :=- 1;
SELECT
	x.`time`,
-- 	IFNULL( d.create_time, x.`time` ) AS `create_time`,
	IFNULL( d.total, 0 ) AS total
FROM
	(
	SELECT
		date_format( DATE_SUB( NOW( ), INTERVAL ( @i := @i + 1 ) DAY ), '%Y-%m-%d' ) AS `time` 
	FROM
		`user` 
	WHERE
		@i <= 20 
	) x
	LEFT JOIN 
	(
	SELECT
		DATE( u.create_time ) AS create_time,
		count( u.user_id ) AS total
	FROM
		`user` u 
	GROUP BY
		DATE( u.create_time ) 
	) d ON TO_DAYS( x.`time` ) = TO_DAYS( DATE( d.create_time ) ) 
ORDER BY
	x.`time`

扩展

在最终语句中:

  1. NOW() 可以替换成你想要的任意时间;
  2. DATE_SUB 表示从 NOW() 时间起往前多少天的连续日期,如果需要往后的连续日期,改为 DATE_ADD 即可。
  3. 在获得连续日期的语句中,WHERE 条件中的 @i <= 20 表示连续 20 天的日期,数值可自定义,选择自己想要查询的连续时间范围。

总结

确定好自己想要的结果形式,然后慢慢补全,直至获得最终的结果,利用分治法的思想,把一个大问题分解成几个小问题,一步一步解决,最终得出大问题的解。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章