DATE_ADD用于日期加法,DATE_SUB用于减法,二者底层逻辑相同;均需注意日期格式、interval单位特性、索引性能及

DATE_ADD 和 DATE_SUB 的基本用法区别
这两个函数本质是同一套逻辑的正负方向:DATE_ADD 用于加,DATE_SUB 用于减,但底层都依赖相同的 interval 单位和语法。别被名字迷惑——DATE_SUB(date, INTERVAL 1 DAY) 和 DATE_ADD(date, INTERVAL -1 DAY) 效果完全一致,只是可读性上前者更直观。
常见错误是传入字符串日期但没做类型校验,比如 '2023-10-01' 被 MySQL 自动转成 date,但 '2023/10/01' 或 '01-10-2023' 可能变成 NULL(取决于 sql_mode)。务必先用 STR_TO_DATE() 显式转换,或确保输入符合 YYYY-MM-DD 格式。
支持的 interval 类型及易错单位
MySQL 支持的单位不是全都能直接对应自然语言理解。比如 WEEK 是按 7 天算,不考虑星期几起始;MONTH 和 YEAR 会处理月末边界(如 DATE_ADD('2023-01-31', INTERVAL 1 MONTH) 得到 '2023-02-28',不是 '2023-02-31')。
-
QUARTER等价于INTERVAL 3 MONTH,不是“日历季度的第一天” -
DAY_SECOND这类复合单位必须配完整时间值,例如DATE_ADD('2023-01-01 12:00:00', INTERVAL 1 DAY_SECOND)合法,但对纯日期字段(无时分秒)用它会截断时间部分 -
SECOND、MINUTE、HOUR对 DATE 类型字段生效时,结果自动转为 DATETIME,隐式补零(如'2023-01-01'+ 1 小时 →'2023-01-01 01:00:00')
在 WHERE 条件中使用 DATE_ADD/DATE_SUB 的性能陷阱
直接写 WHERE DATE_ADD(create_time, INTERVAL 7 DAY) 会导致 create_time 字段无法走索引——因为对列做了函数运算。正确做法是把函数移到右边,改写为:<code>WHERE create_time 。
同理,查“过去 30 天的数据”,别用 DATE_SUB(NOW(), INTERVAL 30 DAY) ,而应写成 <code>create_time > DATE_SUB(NOW(), INTERVAL 30 DAY),确保索引可用。
如果字段是字符串类型(如 VARCHAR 存的日期),先用 STR_TO_DATE(col, '%Y-%m-%d') 转换再比较,但这种写法必然全表扫描——根本解法是改表结构,把列类型改为 DATE 或 DATETIME。
与 NOW()、CURDATE() 混用时的时区注意点
NOW() 返回带时分秒的本地时区时间,CURDATE() 只返回日期部分且也受时区影响。如果你的服务器时区设为 +08:00,但业务要求按 UTC 时间计算,直接用 NOW() 会偏差 8 小时。
稳妥做法是显式指定时区:
- 用
CONVERT_TZ(NOW(), '+08:00', '+00:00')转成 UTC 后再加减 - 或者统一在连接层设置时区(如 JDBC 的
serverTimezone=UTC),让NOW()行为可控 - 避免混用:不要在同一个表达式里交替用
NOW()和CURDATE(),比如DATE_ADD(CURDATE(), INTERVAL HOUR(NOW()) HOUR)—— 这种写法在跨天时可能出错,因为 CURDATE() 不含时间,而 NOW() 含时间
真正麻烦的不是函数不会用,而是日期字段本身类型混乱、时区配置模糊、又叠加了业务上“自然月”“工作日”这类非数据库原生概念——这些得靠应用层兜底,而不是硬塞进 DATE_ADD 里。


















