extract(year from date)是sql标准函数,兼容性强;year(date)是mysql/sql server专属函数,跨库不兼容。二者均提取年份,但year在postgresql等库中会报错,且where中直接使用会导致索引失效。

EXTRACT(YEAR FROM date) 和 YEAR(date) 有什么区别?
本质都是取年份,但行为受数据库引擎控制:EXTRACT 是 SQL 标准函数,几乎所有主流数据库(PostgreSQL、Oracle、SQL Server 2022+、BigQuery)都支持;YEAR 是 MySQL 和 SQL Server 的专属函数,在 PostgreSQL 或 SQLite 中直接报错 function year(timestamp without time zone) does not exist。
实操建议:
- 跨数据库项目优先用
EXTRACT(YEAR FROM created_at),避免迁移时翻车 - MySQL 单独开发可放心用
YEAR(created_at),写法更短,语义更直白 - 注意
EXTRACT的括号嵌套写法是固定语法,不能写成EXTRACT(created_at, YEAR)—— 这在 PostgreSQL 和 Oracle 中会直接报错
想同时取年份和月份,别连写两个 EXTRACT
常见错误是写两遍 EXTRACT:比如 SELECT EXTRACT(YEAR FROM order_date), EXTRACT(MONTH FROM order_date) FROM orders。这本身没错,但性能不优——尤其当 order_date 是索引字段时,数据库无法有效复用解析逻辑。
更稳妥的做法:
- PostgreSQL 推荐用
DATE_PART('year', order_date)和DATE_PART('month', order_date),语义一致且底层优化更好 - MySQL 可用
YEAR(order_date)+MONTH(order_date),这两个函数能被索引覆盖(前提是没对字段做运算,如YEAR(order_date + INTERVAL 1 DAY)就会失效) - 如果只是分组统计,直接写
GROUP BY EXTRACT(YEAR FROM order_date), EXTRACT(MONTH FROM order_date)更清晰,多数引擎能识别并利用日期索引的前缀
WHERE 条件里用 YEAR() 或 EXTRACT 做过滤,为什么有时不走索引?
根本原因是「函数作用于索引列」导致索引失效。例如 WHERE YEAR(created_at) = 2023,数据库必须对每一行计算 YEAR() 才能比对,无法跳过扫描。
正确姿势:
- 改写为范围查询:
WHERE created_at >= '2023-01-01' AND created_at ,这样 B-tree 索引完全可用 - MySQL 8.0+ 支持函数索引,可建
CREATE INDEX idx_year ON orders ((YEAR(created_at))),但仅限该函数,换EXTRACT就无效 - PostgreSQL 不推荐给
EXTRACT(YEAR FROM ...)单独建函数索引,不如直接用生成列:ALTER TABLE orders ADD COLUMN year_only int GENERATED ALWAYS AS (EXTRACT(YEAR FROM created_at)::int) STORED,再对year_only建普通索引
时间戳带时区时,EXTRACT 返回的是本地年份还是 UTC 年份?
取决于字段类型和数据库行为。关键看 created_at 是 TIMESTAMP WITHOUT TIME ZONE 还是 TIMESTAMP WITH TIME ZONE:
- PostgreSQL 中,
TIMESTAMP WITH TIME ZONE存储为 UTC,但EXTRACT(YEAR FROM ...)默认按当前会话时区解释——也就是说,SET timezone = 'Asia/Shanghai'后查出的年份可能是东八区时间对应的年份 - 如果要强制按 UTC 提取,得先转时区:
EXTRACT(YEAR FROM created_at AT TIME ZONE 'UTC') - MySQL 的
TIMESTAMP类型本质存 UTC,但读取时自动转为系统时区,YEAR()返回的就是这个转换后的年份;而DATETIME类型不涉及时区,YEAR()结果确定
时区问题最容易在跨区域部署或凌晨任务中暴露——比如 UTC 时间 2023-12-31 16:00 对应北京时间 2024-01-01 00:00,同一个时间戳用不同方式提取,年份可能差一年。











