DATE_ADD的MONTH单位按日历月推移而非固定天数,如'2024-01-31'+1 MONTH得'2024-02-29';月末日期跨月会自动截断至目标月最后一天;WHERE中函数作用于字段导致索引失效,应将计算移至右侧;业务需明确“加一个月”的语义并选择合适方法。

DATE_ADD的MONTH单位不是加30天
很多人以为 DATE_ADD('2024-01-31', INTERVAL 1 MONTH) 应该变成 '2024-03-02' 或至少是 '2024-03-01',结果却是 '2024-02-29'。这不是bug,是设计行为:MySQL按**日历月**推移,不是按固定秒数或天数。1月有31天,2月最多29天,所以“加一个月”直接落到2月最后一天。
常见错误现象:
- 用
DATE_ADD(date_col, INTERVAL 30 DAY)和DATE_ADD(date_col, INTERVAL 1 MONTH)混着用,以为效果一样 - 在账单周期、会员到期等强日期对齐场景下,发现“每月1号”逻辑被月末自动修正打乱
月末边界触发自动截断
当起始日期是某月最后一天,而目标月天数更少时,MySQL会把日期“压到”目标月最后一天,而不是报错或溢出。比如:
-
DATE_ADD('2023-03-31', INTERVAL -1 MONTH)→'2023-02-28'(不是3月31日减31天) -
DATE_ADD('2024-01-31', INTERVAL 1 MONTH)→'2024-02-29'(闰年) -
DATE_ADD('2024-01-30', INTERVAL 1 MONTH)→'2024-02-29'(注意:30日也落到了2月29日)
这个行为没法关掉,只能接受或绕开。
WHERE里用DATE_ADD(date_col, INTERVAL 1 MONTH)会让索引失效
如果你写 WHERE DATE_ADD(create_time, INTERVAL 1 MONTH) > NOW(),MySQL无法走 create_time 字段的索引——因为函数作用在字段上,优化器没法做范围估算。
正确做法是把计算移到右边:
- ❌ 错误:
WHERE DATE_ADD(create_time, INTERVAL 7 DAY) >= '2026-09-30' - ✅ 正确:
WHERE create_time >= DATE_ADD('2026-09-30', INTERVAL -7 DAY)
这样 create_time 是独立字段,能命中索引。
跨月计算前必须明确业务语义
“加一个月”到底要表达什么?不同业务含义完全不同:
- 如果是“账单日固定为每月1号”,就别用
INTERVAL 1 MONTH,改用LAST_DAY(DATE_ADD(date_col, INTERVAL 1 MONTH)) + INTERVAL 1 DAY再拼1号 - 如果是“滚动30天窗口”,必须用
INTERVAL 30 DAY,MONTH单位完全不适用 - 如果要“对齐自然月”,比如查“上个月1号到上个月最后一天”,优先用
DATE_SUB(LAST_DAY(NOW()), INTERVAL DAY(LAST_DAY(NOW())) - 1 DAY)而不是靠DATE_ADD推算
最易被忽略的一点:同一句 DATE_ADD 在不同月份执行,返回的“天数偏移量”可能差1~3天。别在关键路径上假设它等于固定天数。


















