DATEDIFF函数基本用法为DATEDIFF(datepart,startdate,enddate),严格按此顺序传参;顺序颠倒会导致负值,属逻辑错误;常见datepart包括YEAR、MONTH、DAY等,不支持WEEK。

DATEDIFF 函数的基本用法和参数顺序
DATEDIFF 要求严格按 DATEDIFF(datepart, startdate, enddate) 顺序传参,顺序反了会得到负值,但很多人没意识到这其实是逻辑错误而非计算错误。比如想算「2024-01-01 到 2024-03-15」之间多少个月,写成 DATEDIFF(MONTH, '2024-03-15', '2024-01-01') 得到 -2,表面看没错,但语义上已偏离“从起点到终点”的本意。
常见 datepart 值包括:YEAR、MONTH、DAY、HOUR、MINUTE、SECOND,注意不支持 WEEK(得用 WEEKDAY 或 ISO_WEEK)。
为什么 DATEDIFF(MONTH, ...) 不等于「自然月数」?
DATEDIFF(MONTH, ...) 只比较年份和月份字段,忽略日部分。例如:DATEDIFF(MONTH, '2024-01-31', '2024-02-01') 返回 1,而 DATEDIFF(MONTH, '2024-01-01', '2024-02-28') 也返回 1 —— 它不判断是否真过了完整一个月,只看“月份编号差”。
- 若需「整月跨度」(如财务周期),应配合
DATEFROMPARTS或DATEADD手动对齐到月初再算 - 若用于年龄计算,
DATEDIFF(YEAR, ...)同样不准,2023-12-31 到 2024-01-01 会直接返回 1,实际才过1天 - 真正安全的年龄计算建议用
DATEDIFF(DAY, ...)+ 除以 365.25,或用DATEADD反向验证
跨时区或含时间部分的日期容易踩的坑
当字段是 DATETIME2 或 DATETIMEOFFSET 类型时,DATEDIFF 仍按完整时间值计算,但结果可能违背直觉。例如:
DATEDIFF(DAY, '2024-01-01 23:00:00', '2024-01-02 01:00:00')
返回 1(因为跨了日界),但如果你只关心日期部分,应先用 CAST(... AS DATE) 截断时间:
DATEDIFF(DAY, CAST('2024-01-01 23:00:00' AS DATE), CAST('2024-01-02 01:00:00' AS DATE))
另外,DATETIMEOFFSET 值参与 DATEDIFF 时,SQL Server 自动转为本地时区再比对,若未显式 AT TIME ZONE 标准化,不同服务器设置可能导致结果不一致。
性能提示:DATEDIFF 能否走索引?
在 WHERE 子句中对列使用 DATEDIFF(如 WHERE DATEDIFF(DAY, CreatedDate, GETDATE()) )会导致该列无法使用索引,因为表达式使列值被“封装”了。更高效写法是把函数移到右边:
WHERE CreatedDate > DATEADD(DAY, -7, GETDATE())
这样 SQL Server 能对 CreatedDate 直接走索引查找。同理,避免在 JOIN 条件或 ORDER BY 中对索引列套用 DATEDIFF。
真正麻烦的是嵌套调用,比如 DATEDIFF(SECOND, DATEADD(HOUR, -1, StartAt), EndAt) —— 这种不仅难读,优化器也几乎放弃推导统计信息,查大表时延迟明显。

















