最可靠的是用 TIMESTAMPDIFF(YEAR, birth_date, CURDATE()),它按完整日期比较而非仅年份,避免生日未到就虚长一岁;PostgreSQL 需 AGE() 配 EXTRACT(YEAR FROM ...);SQL Server 要用 DATEDIFF 加 CASE 判断是否已过生日。

MySQL 中用 TIMESTAMPDIFF 计算精确年龄最可靠
直接用 YEAR(NOW()) - YEAR(birth_date) 会出错——比如生日还没到,年龄就虚长一岁。真正精确的年龄必须考虑「是否已过生日」。TIMESTAMPDIFF 是 MySQL 原生支持的方案,按年计算时自动对齐日期逻辑:
SELECT name, birth_date,
TIMESTAMPDIFF(YEAR, birth_date, CURDATE()) AS age
FROM users;它内部比较的是完整日期(年月日),不是单纯年份相减。注意参数顺序:TIMESTAMPDIFF(unit, start, end),单位是 YEAR,不是 YEARS(少个 s 就报错)。
PostgreSQL 需用 AGE() + EXTRACT 拆解年份
PostgreSQL 没有内置「整年差」函数,AGE() 返回的是 interval 类型(如 32 years 5 mons 12 days),不能直接当数字用。必须配合 EXTRACT 提取年份部分:
SELECT name, birth_date,
EXTRACT(YEAR FROM AGE(CURRENT_DATE, birth_date))::INT AS age
FROM users;关键点:
- AGE(end, start) 参数顺序和 MySQL 相反(先当前日期,再出生日期)
- EXTRACT 返回的是 numeric,需转成 ::INT 否则可能带小数
- 如果 birth_date 是 NULL,结果也是 NULL,不报错但要留意业务是否允许
SQL Server 要避开 DATEDIFF(YEAR, ...) 的陷阱
DATEDIFF(YEAR, birth_date, GETDATE()) 看似简洁,但它只比对年份字段,完全忽略月日——12 月 31 日出生的人,1 月 1 日就涨一岁。正确做法是用条件判断:
SELECT name, birth_date,
DATEDIFF(YEAR, birth_date, GETDATE()) -
CASE
WHEN DATEADD(YEAR, DATEDIFF(YEAR, birth_date, GETDATE()), birth_date) > GETDATE()
THEN 1 ELSE 0
END AS age
FROM users;逻辑是:先算年份差,再检查「把出生日期加上这个年份差后,是否超过了今天」;如果超过了,说明生日还没到,得减 1。这个写法虽长,但语义清晰、可读性强,也避免了 TRY_CAST 或字符串拼接等更绕的方案。
跨数据库兼容性差,别硬套同一段 SQL
没有一种写法能在 MySQL / PostgreSQL / SQL Server / Oracle 里通用。哪怕都叫「计算年龄」,函数名、参数顺序、空值处理、闰年逻辑都不同。如果你的应用要支持多数据库:
- 不要在 SQL 层硬算年龄,改在应用层用语言原生日期库(如 Python 的 dateutil.relativedelta、Java 的 Period.between())计算后传入
- 如果必须 SQL 计算,至少按数据库类型做方言适配,别指望一个查询走天下
- 特别注意 birth_date 字段类型:必须是 DATE 或带时区的 TIMESTAMP,如果是字符串(如 '1990-05-20'),某些库会隐式转换失败或精度丢失

















