在 MySQL 中计算日期差是开发中的常见操作,但也是容易踩坑的高频场景。这里总结了几处关键注意事项:DATEDIFF() 返回的是忽略时间的整数天数差,参数顺序直接决定结果的正负;如果需要考虑时间精度,必须改用 TIMESTAMPDIFF();另外,在 WHERE 条件中直接对列名套用函数,会导致索引失效——这一点最容易被忽视,也最容易引发性能问题。

用 DATEDIFF() 函数直接获取整数天数差
DATEDIFF() 是 MySQL 中最直观的日期差计算函数,没有之一。它返回两个日期之间相差的完整天数,仅识别日期部分,时间部分直接被忽略。结果是一个整数,正负取决于你参数的摆放顺序:DATEDIFF(end_date, start_date)——end 在后,start 在前,如果 end 晚于 start,则返回正数。
举例说明:DATEDIFF('2024-01-05', '2024-01-01') 返回的是 4,不是 5。传入参数时,MySQL 会自动识别 '2024-01-01 14:30:00' 这样的格式,但只取日期部分,时间直接被丢弃。所以,想连同时间一起计算?别指望它。
有几个坑需要提前注意:
- 不能传入
NULL,否则整个结果直接变为NULL。建议在排序前先用IFNULL()或COALESCE()做兜底处理。 - 不支持毫秒级精度,时区也不纳入考虑。哪怕字段是
TIMESTAMP类型,它也只关注日期那几位数字。
需要包含时间精度时改用 TIMESTAMPDIFF()
场景变了:想计算“从 2024-01-01 10:00:00 到 2024-01-02 15:30:00 差了多少小时”,DATEDIFF() 就完全无能为力了。这时候必须请出 TIMESTAMPDIFF()——它支持按指定单位进行精确计算,单位可以是 DAY、HOUR、MINUTE,甚至 MONTH、YEAR。
语法是 TIMESTAMPDIFF(unit, datetime_expr1, datetime_expr2),注意顺序:第二个参数作为起点,第三个参数作为终点,与 DATEDIFF() 保持一致。例如 TIMESTAMPDIFF(HOUR, '2024-01-01 10:00:00', '2024-01-02 15:30:00') 返回 27,非常精确。
单位必须大写,支持的枚举值有:SECOND、MINUTE、HOUR、DAY、WEEK、MONTH、QUARTER、YEAR。
有一个隐蔽的坑:如果两个 datetime 值类型不一致——比如一个是 DATE,一个是 DATETIME——MySQL 会进行隐式转换,DATE 被当作当天零点,这样容易莫名其妙地多算或少算一整天。
WHERE 条件里计算日期差要小心索引失效
很多新手喜欢在查询中写 WHERE DATEDIFF(NOW(), created_at) > 30,看起来挺直观。但这样写,created_at 字段上的索引就直接失效了——函数套在列上,优化器无法进行范围扫描。
正确的做法是把函数移到等号右边:WHERE created_at < DATE_SUB(NOW(), INTERVAL 30 DAY)。如果想更简洁,可以写成 WHERE created_at < NOW() - INTERVAL 30 DAY。核心原则就一条:表达式左边必须是裸列名,不带任何函数或运算。
同理,TIMESTAMPDIFF(DAY, created_at, NOW()) > 30 也会导致索引失效,必须改写成 created_at < NOW() - INTERVAL 30 DAY。
跨月/跨年时 DATEDIFF() 和 TIMESTAMPDIFF(MONTH) 行为不同
这两个函数在跨月场景下完全是两套逻辑。DATEDIFF() 只认日历天数,而 TIMESTAMPDIFF(MONTH, ...) 计算的是“月份差”,不是简单除以 30 那么粗暴。比如 1 月 31 日到 2 月 28 日(非闰年),DATEDIFF() 返回 28 天,但 TIMESTAMPDIFF(MONTH, '2024-01-31', '2024-02-28') 返回 0——因为没满一个月。
TIMESTAMPDIFF(MONTH, d1, d2) 的内部逻辑是:先计算年份差乘以 12,再加上月份差,最后看日部分是否满足条件——只有 d2 的日 ≥ d1 的日时,才计入这个月。
所以 TIMESTAMPDIFF(MONTH, '2024-01-15', '2024-02-14') 结果是 0,而 '2024-01-15' 到 '2024-02-15' 才是 1。
千万别指望用 DATEDIFF() 除以 30 来模拟月差,误差会大到离谱。也别妄想用 TIMESTAMPDIFF(MONTH, ...) 反向推算出具体的天数——它根本不保证可逆。
实际业务中,最容易被忽略的无非就是隐式类型转换和索引失效这两点。尤其当表的数据量增大之后,一个没留神的 DATEDIFF() 写法,可能让原本毫秒级的查询直接飙到数秒,教训确实不少。
