正确写法是where create_time >= date_sub(now(), interval 30 day),因=仅匹配单时刻、between易边界遗漏;函数作用于常量时索引有效,作用于字段则全表扫描。

DATE_SUB 用法和常见错误
直接写 DATE_SUB(NOW(), INTERVAL 30 DAY) 是对的,但很多人在 WHERE 条件里漏掉比较方向,导致查不到数据。比如写成 WHERE create_time = DATE_SUB(...),这只会匹配某一天的零点,不是“最近30天”。正确逻辑是:字段要大于等于这个时间点。
WHERE 条件必须用 >= 而不是 = 或 BETWEEN
MySQL 的 DATE_SUB 返回的是带时间的 datetime 值(如 2024-05-01 14:22:33),而业务数据通常有精确到秒的时间戳。用 = 会严格匹配单个时刻,几乎总为空;用 BETWEEN 容易因边界时间引发遗漏(比如 BETWEEN '2024-05-01' AND NOW() 实际只到当天 00:00:00)。最稳写法是:
WHERE create_time >= DATE_SUB(NOW(), INTERVAL 30 DAY)
- 确保
create_time是DATETIME或TIMESTAMP类型,不是DATE - 如果字段是
DATE类型(只存年月日),得用DATE(NOW())配合:WHERE create_date >= DATE_SUB(DATE(NOW()), INTERVAL 30 DAY) - 注意时区:如果数据库用 UTC,而业务按本地时间算“最近30天”,得先转换时区再计算
索引是否生效?关键看字段和函数位置
DATE_SUB 本身不阻止索引使用,但前提是它不能包在字段上。下面两种写法效果天差地别:
- ✅ 索引有效:
WHERE create_time >= DATE_SUB(NOW(), INTERVAL 30 DAY)(函数作用于常量) - ❌ 索引失效:
WHERE DATE_SUB(create_time, INTERVAL 30 DAY) >= '2024-04-01'(函数作用于字段,触发全表扫描)
执行前务必用 EXPLAIN 看 key 列是否有索引名,type 是否为 range。
跨月或跨年时 DATE_SUB 会不会出错?
不会。DATE_SUB 是 MySQL 内置日期函数,自动处理大小月、闰年等。例如 DATE_SUB('2024-03-01', INTERVAL 30 DAY) 正确返回 2024-02-01,不是 2024-02-00。但要注意:如果你用字符串拼接构造日期(比如 CONCAT(YEAR(NOW())-1, '-01-01')),那就容易出错——这种写法绕过了日期运算逻辑,应避免。
真正容易被忽略的是:30 天 ≠ 上月同期。比如今天是 3 月 31 日,DATE_SUB 往前推 30 天是 3 月 1 日,不是 2 月 31 日(不存在),而是自动回滚到 3 月 1 日。这点和人类直觉略有偏差,但符合 SQL 标准行为。










