最稳妥方式是用 date_sub(curdate(), interval n day) 配合 group by date(时间字段),避免硬编码日期;原始字段为 datetime/timestamp 时需用 date() 转换以实现天粒度分组,且 where 应采用左闭右开区间确保7天数据完整。

直接说结论:用 DATE_SUB(CURDATE(), INTERVAL n DAY) 配合 GROUP BY DATE(时间字段) 是最稳妥的方式,别硬算“7天前的日期字符串”
为什么不能直接写 WHERE date_time >= '2024-06-01'?
硬编码日期会导致查询不可复用,且容易漏掉时区、数据延迟、跨月边界等问题。真实生产环境必须用动态日期计算。
- MySQL 的
CURDATE()返回的是YYYY-MM-DD格式,不带时间,适合做“天粒度”截断 - 如果原始字段是
DATETIME或TIMESTAMP,直接GROUP BY date_time会按秒分组,必须先转成日期 - 用
DATE(date_time)比DATE_FORMAT(date_time, '%Y-%m-%d')更轻量,函数开销小,还能走索引(如果该字段有前缀索引或函数索引)
如何确保只查过去7天(含当天),且每天一条记录不遗漏?
关键在 WHERE 条件范围要闭合:从 7 天前的 00:00:00 到今天的 23:59:59。但更推荐用左闭右开方式,避免时间精度问题。
- 正确写法:
WHERE date_time >= DATE_SUB(CURDATE(), INTERVAL 6 DAY) AND date_time - 解释:取从“6天前的今天 00:00”开始,到“明天 00:00”之前——这样刚好覆盖最近7个自然日(比如今天是 6 月 7 日,就查 6 月 1 日至 6 月 7 日)
- 如果用
INTERVAL 7 DAY,实际是查 6 月 1 日 00:00 之前的数据,会少掉 6 月 1 日全天 - 注意:若业务要求“最近7×24小时”,则应改用
NOW() - INTERVAL 7 DAY,但这时 GROUP BY 得用DATE(date_time),结果可能跨自然日(如下午跑数,6 月 1 日只统计了下午)
完整可执行 SQL 示例(MySQL)
SELECT DATE(date_time) AS stat_date, COUNT(DISTINCT user_id) AS active_users FROM user_action_log WHERE date_time >= DATE_SUB(CURDATE(), INTERVAL 6 DAY) AND date_time <p>说明:</p>
-
COUNT(DISTINCT user_id)防止同一用户一天内多次行为被重复计数 - 如果表很大,确保
date_time字段上有索引;若只有(user_id, date_time)联合索引,效果可能打折扣 - 某些旧版 MySQL 不支持函数索引,
DATE(date_time)无法走索引——此时可考虑加一个生成列stat_date DATE AS (DATE(date_time)) STORED并建索引
最容易被忽略的一点:如果你的数据库时区不是 UTC 或系统本地时区,CURDATE() 和 date_time 值可能不在同一时区下对齐。上线前务必用 SELECT NOW(), CURDATE(), @@time_zone; 对齐验证。










