正确写法是WHERE created_at >= DATE_SUB(NOW(), INTERVAL 7 DAY) AND status = 'valid',需确保created_at为DATETIME/TIMESTAMP类型、建立(created_at, status)复合索引,并验证状态值大小写与空格一致性。

WHERE条件里怎么写时间范围和状态联合筛选
直接用 created_at >= DATE_SUB(NOW(), INTERVAL 7 DAY) 和 status = 'valid' 组合,但要注意时区和字段类型。如果 created_at 是 DATETIME 或 TIMESTAMP,这个写法没问题;如果是字符串(比如 VARCHAR 存的 '2024-05-01 10:20:30'),得先转成日期再比较,否则可能走全表扫描或结果不准。
常见错误是写成 created_at > NOW() - 7 —— 这在 MySQL 里会把 NOW() 当数字减,结果完全不对。
- 推荐写法:
WHERE created_at >= DATE_SUB(NOW(), INTERVAL 7 DAY) AND status = 'valid' - 如果数据库用的是 PostgreSQL,换成:
WHERE created_at >= NOW() - INTERVAL '7 days' AND status = 'valid' - SQL Server 用户注意:
DATEADD(day, -7, GETDATE())才是等效写法,别用DATEADD(dd, -7, ...)—— 虽然能跑,但dd是过时别名,官方文档已不推荐
索引没建对,查询慢到怀疑人生
光写对 WHERE 没用,如果表里没在 (created_at, status) 上建复合索引,或者只单建了 created_at 索引,MySQL 很可能无法高效过滤状态字段,最终还是扫大表。
订单表通常数据量大,status 区分度低(比如只有 'valid'/'invalid'/'pending'),单独为它建索引意义不大;但和时间字段组合后,就能快速定位“近7天+有效”这一高频查询区间。
- 建索引命令:
CREATE INDEX idx_created_status ON orders (created_at, status); - 别反过来建
(status, created_at)—— 因为status = 'valid'返回太多行,MySQL 仍需回表筛时间,效果差很多 - 执行前用
EXPLAIN SELECT ...看key列是否命中这个索引,rows是否明显下降
“近7天”到底包不包括今天?边界容易错
用 DATE_SUB(NOW(), INTERVAL 7 DAY) 是从当前时刻往前推168小时,比如现在是 2024-05-10 14:30:00,那起始点就是 2024-05-03 14:30:00 —— 这意味着 5月3日 14:29 的订单查不到。业务上常要“最近7个自然日”,就得按天截断:
- 要包含今天零点开始的所有订单:
created_at >= DATE(NOW()) - INTERVAL 6 DAY(即从 00:00:00 开始) - 更清晰写法(推荐):
created_at >= DATE_SUB(CURDATE(), INTERVAL 6 DAY),因为CURDATE()永远返回当天 00:00:00 - 如果业务要求严格“过去168小时内”,就坚持用
NOW();但报表类需求多数按日,别让下游同学半夜跑数时发现缺了一小时数据
状态字段值大小写和空格问题
status = 'valid' 看似简单,但实际表里可能是 'Valid'、'VALID '(带空格)、甚至 'active'(命名不统一)。线上出过因测试环境用小写、生产环境用大写导致查询为空的事故。
- 先确认真实值:
SELECT DISTINCT status FROM orders WHERE created_at >= DATE_SUB(NOW(), INTERVAL 7 DAY) LIMIT 10; - 保险起见可加
TRIM(UPPER(status)) = 'VALID',但会跳过索引 —— 所以最好源头规范数据写入 - 如果状态是枚举型,建议在建表时用
ENUM('valid', 'invalid', 'pending')或加CHECK约束,比靠应用层校验更可靠
业务逻辑越简单,越要抠细节:时间函数选错、索引顺序颠倒、状态值不一致——这三个点,任何一个没对齐,查出来的就不是你要的“近7天有效订单”。

















