DATEDIFF函数不返回自然天数,而是统计“边界跨越次数”:SQL Server按午夜线计数,MySQL自动截断时间只比日期,PostgreSQL则无此函数需用减法。

DATEDIFF 少算一天,不是函数坏了,是它根本就不按“自然天数”设计——它算的是“边界跨越次数”,而你想要的是“包含起始日的完整天数”。
SQL Server 的 DATEDIFF(day, start, end) 为什么返回 0 而不是 1?
比如用户注册日是 '2024-03-15',今天也是 '2024-03-15',DATEDIFF(day, '2024-03-15', GETDATE()) 返回 0。
原因:它统计的是「从 start 到 end 之间跨过了多少个午夜」。同一天内,没跨午夜,结果就是 0。
- 要得到“已注册第 X 天”这种业务含义(即包含起始日),必须手动加 1:
DATEDIFF(day, start, end) + 1 - 如果 end 是
'2024-03-15 23:59:59'、start 是'2024-03-15 00:00:01',结果仍是 0 —— 时间再长,只要没跨日,就不计 - 别用
day单位去判断“是否满 24 小时”,该换SECOND或TIMESTAMPDIFF(SECOND, ...)
MySQL 的 DATEDIFF(end, start) 为什么看起来“少一天”?
常见错觉:传入带时间的值,比如 DATEDIFF('2024-03-15 10:00', '2024-03-14 20:00') 返回 1,但实际差了约 14 小时,你可能预期是 0 或小数。
真相:MySQL 的 DATEDIFF **自动截断时间部分**,只比日期 —— 上例等价于 DATEDIFF('2024-03-15', '2024-03-14'),所以是 1。
- 这不是“少算”,是“故意忽略时间”。想保留时间精度,必须用
TIMESTAMPDIFF(DAY, start, end)(注意参数顺序是unit, start, end) - 字段类型是
DATETIME时,别直接丢进DATEDIFF,先显式转日期:DATEDIFF(DATE(created_at), '2024-03-01') - 字符串格式务必用
'YYYY-MM-DD','03/15/2024'在某些 locale 下会被解析成 2024-15-03,直接报错或错乱
WHERE 条件里写 DATEDIFF(...) 导致逻辑偏差
例如写 WHERE DATEDIFF(CURDATE(), order_date) 查“近 7 天订单”,结果漏掉今天下单的记录?
因为 DATEDIFF(CURDATE(), order_date) 对今天的数据返回 0, 是包含的;但如果你本意是“过去 7×24 小时内”,那 <code>DATEDIFF 就完全失准 —— 它不管小时,只看日期。
- 真正要查“最近 7 天”,应写:
WHERE order_date >= DATE_SUB(CURDATE(), INTERVAL 7 DAY) - 要查“下单后超 24 小时未支付”,不能用
DATEDIFF,得用:TIMESTAMPDIFF(HOUR, order_time, NOW()) > 24 - 所有对字段套函数的 WHERE 条件(如
DATEDIFF(NOW(), created_at))都会让索引失效,性能和语义双崩
PostgreSQL 根本没有 DATEDIFF,硬写就报错
直接写 DATEDIFF('day', a, b) 会触发错误:function datediff(unknown, timestamp, timestamp) does not exist。
它用原生减法:end_date::date - start_date::date 返回整数天数,语义清晰且可索引。
- 两个
DATE相减 → 整数天数(最安全) - 两个
TIMESTAMP相减 →INTERVAL,再用EXTRACT(EPOCH FROM (end - start)) / 86400.0得带小数的天数 - 别用
AGE()替代 —— 它返回年月日结构,不适合数值计算或比较
跨数据库移植时,最容易被忽略的不是语法,而是“DATEDIFF 这个名字在不同库里根本不是同一个东西”:MySQL 截时间、SQL Server 数边界、PostgreSQL 压根没有。写之前,先确认你连的是哪个库,再查它的文档,而不是凭印象敲函数名。

















