YEAR和MONTH函数跨数据库兼容性差:MySQL/SQL Server支持但规则不同,PostgreSQL需EXTRACT,SQLite用strftime且返回字符串;空值和非法日期处理不一致;分组排序推荐用年月字符串或数值组合(如202312)。

YEAR 和 MONTH 函数能直接从日期字段中提取整数型年份或月份,但它们在不同数据库中的可用性、输入类型和空值处理逻辑差异很大,不能无脑套用。
MySQL里YEAR(MONTH())可以直接嵌套,但PostgreSQL不认
MySQL 支持 YEAR() 和 MONTH() 作为原生函数,接受 DATETIME 或 DATE 类型,返回整数:
SELECT YEAR(order_date) AS y, MONTH(order_date) AS m FROM orders;
PostgreSQL 没有 YEAR() 或 MONTH() 函数,必须用 EXTRACT():
SELECT EXTRACT(YEAR FROM order_date) AS y, EXTRACT(MONTH FROM order_date) AS m FROM orders;
- SQL Server 用
YEAR()和MONTH(),但不支持对字符串隐式转换,YEAR('2023-05-01')会报错,必须先转成DATE - SQLite 没有这些函数,得靠
strftime('%Y', order_date)和strftime('%m', order_date),注意返回的是字符串,不是数字
遇到NULL或非法日期时,各数据库返回结果不一致
YEAR(NULL) 在 MySQL 和 SQL Server 中返回 NULL;但 SQLite 的 strftime('%Y', NULL) 返回空字符串,可能引发隐式类型转换问题。
- MySQL 对非法日期如
'2023-13-01'默认转为NULL(开启严格模式才报错),所以YEAR('2023-13-01')得到NULL - PostgreSQL 的
EXTRACT(YEAR FROM '2023-13-01'::DATE)直接抛错:ERROR: date/time field value out of range - 生产环境建议加
WHERE order_date IS NOT NULL AND order_date >= '1970-01-01'过滤掉脏数据,别依赖函数兜底
想按年月分组统计?别只用YEAR/MONTH,小心跨年排序错乱
单独用 YEAR(order_date) 和 MONTH(order_date) 做 GROUP BY 可以,但如果要按“年-月”自然顺序排序(比如 2023-12 排在 2024-01 前),仅靠两个字段 ORDER BY YEAR(), MONTH() 是不够的——它会先排年份再排月份,看似没问题,但一旦涉及多表 JOIN 或窗口函数,容易漏掉时间连续性约束。
- 更稳妥的方式是生成标准年月标识:
DATE_FORMAT(order_date, '%Y-%m')(MySQL)、TO_CHAR(order_date, 'YYYY-MM')(PostgreSQL)、FORMAT(order_date, 'yyyy-MM')(SQL Server) - 如果只关心年份+月份的数值组合,可用
YEAR(order_date) * 100 + MONTH(order_date),结果如 202312,天然支持数字排序 - 注意:这个算式在 SQLite 里不适用,因为没有
YEAR(),得写成CAST(strftime('%Y', order_date) AS INTEGER) * 100 + CAST(strftime('%m', order_date) AS INTEGER)
跨数据库写法最不可靠的就是硬写 YEAR() —— 它看起来像标准 SQL,实则几乎每个系统都自己实现了一套。真正要兼容,要么用条件宏预处理,要么统一走字符串格式化函数,再做类型转换。别低估一个年份提取背后隐藏的类型系统分歧。

















