DATEDIFF(YEAR, ...) 返回年份跨度而非满年数,正确做法是用 DATEADD(YEAR, N, date) 与当前日期比较;SQL Server 推荐 CASE + DATEADD 判断,MySQL 用 TIMESTAMPDIFF(YEAR, ...),PostgreSQL 用 EXTRACT(YEAR FROM AGE()),均自动处理闰年边界。

DATEDIFF(YEAR, ...) 不能直接算“满多少年”
SQL Server 的 DATEDIFF(YEAR, ...) 只比较年份部分,不看月日。比如 2020-12-31 到 2021-01-01,DATEDIFF(YEAR, '2020-12-31', '2021-01-01') 返回 1,但实际只隔了 1 天——这显然不是“满 1 年”。很多业务场景(如工龄补贴、试用期判断)要求严格按自然日历差计算是否“已满 X 年”,必须避开这个陷阱。
- 只用
DATEDIFF(YEAR, ...)是错的,它返回的是“年份跨度数”,不是“完整年数” - 正确逻辑是:从入职日期起,往后推 N 年,看当前日期是否 ≥ 那个日期
- PostgreSQL/MySQL/Oracle 不支持直接在
DATEADD或INTERVAL中用变量年数做动态加法,得换思路
SQL Server 推荐写法:用 DATEADD + IIF 判断是否满年
核心是先算出“入职日期 + N 年”是哪天,再和今天比。例如判断是否满 3 年:GETDATE() >= DATEADD(YEAR, 3, hire_date)。把它封装成逐级判断,就能得出“满几年”:
SELECT
name,
hire_date,
CASE
WHEN GETDATE() >= DATEADD(YEAR, 3, hire_date) THEN 3
WHEN GETDATE() >= DATEADD(YEAR, 2, hire_date) THEN 2
WHEN GETDATE() >= DATEADD(YEAR, 1, hire_date) THEN 1
ELSE 0
END AS full_years
FROM employees;
- 适用于 SQL Server 2012+;
GETDATE()可换成SYSDATETIME()提高精度 - 不能直接用
DATEDIFF算出整数再减 1,因为跨闰年、2 月 29 日等边界会出错 - 如果要算“满 5 年以上”,别写
>= 5,而应单独加一条WHEN GETDATE() >= DATEADD(YEAR, 5, hire_date) THEN 5+,避免漏掉临界点
MySQL / PostgreSQL 兼容写法:用 TIMESTAMPDIFF 或 AGE
MySQL 提供 TIMESTAMPDIFF(YEAR, hire_date, CURDATE()),它内部做了月日对齐校验,结果是“完整年数”,比 DATEDIFF 安全得多;PostgreSQL 用 EXTRACT(YEAR FROM AGE(CURRENT_DATE, hire_date)) 同理。
- MySQL:
TIMESTAMPDIFF(YEAR, '2020-12-31', '2021-01-01')返回0(正确),而DATEDIFF(YEAR, ...)返回1 - PostgreSQL:
AGE()返回 interval 类型,EXTRACT(YEAR FROM ...)取整年部分,自动向下取整 - 注意:所有这些函数都基于当前日期(
CURDATE()/CURRENT_DATE),测试时若用固定日期需显式传入
容易被忽略的边界:2 月 29 日入职怎么办?
如果员工在闰年 2 月 29 日入职,非闰年时系统如何认定“满 1 年”?SQL Server 的 DATEADD(YEAR, 1, '2020-02-29') 会返回 2021-02-28(不是报错),所以 2021-02-28 当天就算满 1 年;MySQL 的 TIMESTAMPDIFF 行为一致。这意味着:2 月 29 日入职者,在平年 2 月 28 日即视为满年,无需额外处理。
- 不用手动补
DAYOFYEAR换算,数据库引擎已内置该逻辑 - 但 HR 系统若要求“必须到 3 月 1 日才算”,那就得脱离纯 SQL,用应用层控制——SQL 只负责通用日历逻辑
- 上线前务必用
'2020-02-29'、'2021-02-28'、'2021-03-01'这组数据验证结果

















