因为date_sub(now(), interval 1 day)返回的是当前时刻减24小时的精确时间戳(如2026-08-11 11:09:00 → 2026-08-10 11:09:00),而非自然日起点,导致2026-08-10 00:00:00至11:08:59的数据被遗漏;根本原因是瞬时时间函数与“昨日自然日”语义错配。

为什么DATE_SUB(NOW(), INTERVAL 1 DAY)会漏掉零点记录
它返回的是当前时刻减24小时的精确时间戳,不是“昨天00:00:00”。比如现在是2026-08-11 11:09:00,DATE_SUB(NOW(), INTERVAL 1 DAY)结果就是2026-08-10 11:09:00,导致2026-08-10 00:00:00到2026-08-10 11:08:59之间的数据全被跳过。
根本问题在于:用瞬时时间函数做跨天边界,语义错配。你想要的是“自然日”,它给的是“相对时刻”。
WHERE DATE(create_time) = '2026-08-10'为什么不能随便用
这个写法逻辑正确,能查出当天所有记录,但代价是索引失效——MySQL无法对create_time字段直接走B+树索引,必须全表扫描。
- 如果表有百万级数据,查询可能从几毫秒变成几秒
- 高并发下容易拖垮数据库
- 执行计划里会看到
type: ALL和Extra: Using where
真正安全高效的范围写法是什么
用“左闭右开”区间:>=起始时间,次日零点。既覆盖全天,又可走索引。
示例(查2026-08-10全天):
SELECT * FROM orders WHERE create_time >= '2026-08-10 00:00:00' AND create_time <p>动态生成(推荐):</p><pre class="brush:php;toolbar:false;">SELECT * FROM orders WHERE create_time >= DATE(NOW()) - INTERVAL 1 DAY AND create_time
-
DATE(NOW()) - INTERVAL 1 DAY返回2026-08-10,隐式转为2026-08-10 00:00:00 -
DATE(NOW())返回2026-08-11,隐式转为2026-08-11 00:00:00 - 全程不调用函数作用于字段,索引可用
时区和字段类型不匹配也会丢数据
如果create_time是TIMESTAMP类型,而客户端或会话时区设置为+00:00,但业务按+08:00理解时间,那'2026-08-10 00:00:00'实际存进库的是UTC时间2026-08-09 16:00:00,查询就天然偏移8小时。
检查方式:
SELECT @@global.time_zone, @@session.time_zone;
修复建议:
- 统一设置会话时区:
SET time_zone = '+08:00'; - 或改用
CONVERT_TZ()显式转换:CONVERT_TZ(create_time, '+00:00', '+08:00')(仅在必要时,仍影响索引) - 更稳妥的做法:存储用
TIMESTAMP,但所有写入/查询都基于UTC,业务层负责时区转换
最常被忽略的点:日期边界必须和字段类型、时区、索引三者对齐。少一个,就可能漏掉凌晨的数据,而且很难复现——因为只在特定时间点执行才暴露。











