最稳方式是DATE_SUB(CURRENT_DATE, INTERVAL 1 DAY),它返回纯日期值,适配DATE类型字段;对DATETIME/TIMESTAMP字段须用范围查询,避免用CURRENT_DATE-1或NOW()-INTERVAL 1 DAY。

查昨天数据用 DATE_SUB(CURRENT_DATE, INTERVAL 1 DAY) 最稳
MySQL 里查“昨天”的数据,最直接可靠的方式就是用 DATE_SUB(CURRENT_DATE, INTERVAL 1 DAY)。它不依赖时区设置、不拼字符串、不绕弯子,返回的就是标准日期值,能直接和 DATE 类型字段做等值或范围比较。
常见错误是写成 CURRENT_DATE - 1 或 NOW() - INTERVAL 1 DAY:前者在某些 MySQL 版本里会变成数值减法(比如 20240520 - 1 = 20240519,看似对但本质是整数运算,不可靠);后者带时间部分,容易漏掉凌晨数据。
-
CURRENT_DATE返回的是纯日期(YYYY-MM-DD),没有时分秒,和业务表中常用DATE字段类型天然对齐 - 如果字段是
DATETIME或TIMESTAMP,别直接等于昨天的日期值——得用范围:WHERE created_at >= DATE_SUB(CURRENT_DATE, INTERVAL 1 DAY) AND created_at - 避免用
DATE(created_at) = DATE_SUB(...),这会让索引失效,尤其在大表上明显变慢
WHERE 条件里别用函数包裹索引字段
这是性能最容易被踩的坑。比如表里 order_date 是 DATE 类型且有索引,但写成 WHERE DATE(order_date) = DATE_SUB(...),MySQL 就没法走索引。
正确做法是让索引字段“裸奔”,把计算放在右边:
WHERE order_date = DATE_SUB(CURRENT_DATE, INTERVAL 1 DAY)
如果是 DATETIME 字段,同样要避免 DATE() 包裹:
WHERE created_at >= '2024-05-20' AND created_at < '2024-05-21'
- 上面这个写法可以命中
created_at上的索引(B+Tree 范围扫描) - 用
DATE_SUB(CURRENT_DATE, INTERVAL 1 DAY)动态生成左边界更安全,避免硬编码日期 - 注意:如果数据库时区和应用不一致,
CURRENT_DATE取的是 MySQL server 的时区,不是你本地机器的
不同 MySQL 版本对 CURRENT_DATE 的行为一致吗
一致。从 5.6 到 8.0,CURRENT_DATE 都是标准 SQL 函数,语义稳定,返回 server 当前日期(不含时间),且不受 STRICT_TRANS_TABLES 等模式影响。
但要注意两个实际差异点:
- MySQL 5.7 之前不支持
CAST(CURRENT_DATE AS DATE)这种冗余写法(虽无害,但没必要) - 如果用的是阿里云 RDS 或腾讯云 CDB,确认没开启“只读实例自动跳过函数计算”类优化(极少见,但个别定制版有)
- 用
SELECT CURRENT_DATE, NOW(), SYSDATE()对比一下,能快速验证当前环境时区和精度是否符合预期
PostgreSQL 或 SQLite 用户别抄这个写法
标题里明确是 SQL,但实际执行引擎决定语法。MySQL 的 DATE_SUB 在 PostgreSQL 里对应的是 CURRENT_DATE - INTERVAL '1 day',SQLite 是 date('now', '-1 day')。
如果你在跨数据库写通用逻辑(比如 ORM 层),别硬写原生 MySQL 函数。ORM 如 SQLAlchemy、MyBatis 通常封装了日期偏移方法,比手拼 SQL 更安全。
真要写原生 SQL 且需兼容,优先考虑 ANSI 标准写法:CURRENT_DATE - 1 在 PostgreSQL 和 SQL Server 都行,但在 MySQL 里又回到前面说的风险点——所以最保险的,还是按目标数据库选函数。
日期计算看着简单,但一动就牵扯时区、字段类型、索引策略、版本兼容。哪怕只是查一天,也得先看清楚字段存的是什么、有没有索引、你的 MySQL 实例跑在哪台机器上。










