<p>DATEDIFF函数参数顺序决定正负:SQL Server/Azure SQL为DATEDIFF(datepart, startdate, enddate),结果= enddate - startdate;MySQL则为DATEDIFF(enddate, startdate),二者不兼容;PostgreSQL无该函数,直接用enddate - startdate。</p>

DATEDIFF 函数的参数顺序决定结果正负
SQL 中 DATEDIFF 不是“大减小”,而是严格按 DATEDIFF(datepart, startdate, enddate) 计算:结果 = enddate - startdate。很多人传反参数,导致天数为负却没意识到——尤其在写 WHERE 条件(如 DATEDIFF(day, order_date, GETDATE()) > 30)时,负值会让条件永远不成立。
常见错误现象:DATEDIFF(day, '2024-05-10', '2024-05-01') 返回 -9,不是 9;若用于判断“是否超期”,逻辑就彻底反了。
- MySQL 用户注意:
DATEDIFF(enddate, startdate)参数顺序相反,是enddate - startdate,和 SQL Server / PostgreSQL 的DATEDIFF不兼容 - SQL Server 和 Azure SQL 必须用
day、dd或d作为datepart,写'day'字符串会报错 - PostgreSQL 没有
DATEDIFF,直接用enddate - startdate(两个DATE相减得整数天),更简单但不可跨平台迁移
跨月/跨年计算时,DATEDIFF(day, ...) 仍是精确天数差
有人误以为 DATEDIFF 像 Excel 的 DATEDIF 那样支持 “ym”、“md” 等模糊单位,其实只要 datepart 是 day(或等价写法),它就纯粹算日历天数,不考虑月份长度或闰年逻辑——而这恰恰是它的优势:稳定、可预期。
例如:DATEDIFF(day, '2023-02-28', '2024-02-28') 返回 366(因含 2024-02-29),DATEDIFF(day, '2023-01-01', '2024-01-01') 返回 365。不需要手动处理闰年。
- 避免用
month或year代替day来“估算”天数,比如DATEDIFF(month, a, b) * 30—— 误差可能达 ±3 天 - 如果字段是
DATETIME或TIMESTAMP,DATEDIFF(day, ...)会自动截断时间部分,只比对日期(即2024-05-01 23:59:59和2024-05-02 00:00:01相差 1 天)
WHERE 子句中用 DATEDIFF 要警惕索引失效
在过滤条件里写 WHERE DATEDIFF(day, created_at, GETDATE()) > 7,数据库通常无法走 created_at 上的索引,因为函数作用于列上。数据量一大,查询就会变慢。
正确做法是把函数移到右边,让左边保持裸列:
WHERE created_at < DATEADD(day, -7, GETDATE())
这样优化器能识别范围扫描,命中索引。
- MySQL 对应写法:
WHERE created_at < DATE_SUB(NOW(), INTERVAL 7 DAY) - PostgreSQL:
WHERE created_at < NOW() - INTERVAL '7 days' - 如果业务必须用
DATEDIFF(比如报表脚本统一风格),至少确保该字段有索引,并评估执行计划中的Index Seek是否被降级为Index Scan
NULL 值会让整个 DATEDIFF 结果变成 NULL
这是最容易被忽略的隐性 bug。只要 startdate 或 enddate 任一为 NULL,DATEDIFF(day, startdate, enddate) 就返回 NULL,而不是 0 或报错。在聚合(如 AVG(DATEDIFF(...)))或条件判断中,NULL 可能被静默丢弃,导致统计偏差。
解决方法不是靠外围 ISNULL 包裹函数结果,而应在源头处理缺失逻辑:
- 明确业务含义:未填写的日期是“未知”还是“视为今天”?前者应保留
NULL并在应用层处理;后者才用ISNULL(enddate, GETDATE()) - 写 WHERE 时别用
DATEDIFF(...) = NULL,要用DATEDIFF(...) IS NULL—— 后者才是标准写法 - 在视图或 CTE 中提前用
CASE WHEN startdate IS NULL OR enddate IS NULL THEN NULL ELSE DATEDIFF(...) END显式声明空值策略
实际用的时候,先盯死参数顺序,再看字段有没有 NULL,最后检查 WHERE 里有没有无意中干掉索引——这三步走完,DATEDIFF 就很难出错了。


















