EXTRACT(YEAR FROM date) 并不比 YEAR(date) 更高效,而是更符合SQL标准、跨数据库兼容;但二者在WHERE中对索引列使用均会导致B-tree索引失效,应改用范围查询如 order_time >= '2023-01-01' AND order_time < '2024-01-01'。

EXTRACT(YEAR FROM date) 本身并不比 YEAR(date) 效率更高
这是个常见误解。EXTRACT 不是“更快”,而是“更可预测”——它不因数据库而异,行为由 SQL 标准约束。MySQL 的 YEAR() 在内部可能做了轻量优化,但 PostgreSQL、Oracle、BigQuery 等根本没这个函数;你硬写 YEAR(created_at),直接报错 function year(timestamp without time zone) does not exist。所谓“效率高”,其实是“能跑通 + 不翻车”的综合结果。
WHERE 中用 EXTRACT(YEAR FROM col) = 2023 会严重拖慢查询
无论用 EXTRACT 还是 YEAR,只要把函数套在索引列上,B-tree 索引就基本失效。数据库必须逐行计算再比对,无法跳过扫描。
- 错误写法:
WHERE EXTRACT('year' FROM order_time) = 2023(PostgreSQL)或WHERE YEAR(order_time) = 2023(MySQL) - 正确替代:
WHERE order_time >= '2023-01-01' AND order_time - MySQL 8.0+ 可建函数索引:
CREATE INDEX idx_year ON orders ((YEAR(order_time))),但仅对该函数有效,换EXTRACT就不认 - PostgreSQL 更推荐生成列:
ALTER TABLE orders ADD COLUMN year_only int GENERATED ALWAYS AS (EXTRACT('year' FROM order_time)::int) STORED,再对year_only建普通索引
EXTRACT 的语法细节一错就报错,不是性能问题而是执行失败
PostgreSQL 和 Oracle 要求单位名必须是带单引号的小写字符串,比如 'year',不是 YEAR、Year 或 extract(year from ...)。
- 报错写法:
EXTRACT(YEAR FROM now())→function extract("unknown", timestamp with time zone) does not exist - 正确写法:
EXTRACT('year' FROM now()),返回2026.0(double precision 类型) - 如需整数参与
GROUP BY或拼接,必须显式转类型:EXTRACT('year' FROM paid_at)::int -
EXTRACT('month' FROM ...)单独用意义极小——6月可能是 2022 年 6 月,也可能是 2023 年 6 月,漏年份就失去业务上下文
真正影响性能的是你怎么用,不是你用哪个函数
跨库项目里坚持用 EXTRACT('year' FROM col),不是因为它快,是因为它:
- 在 PostgreSQL、Oracle、SQL Server 2022+、BigQuery 上都原生支持
- 不依赖引擎私有语法,迁移时不用全局替换
YEAR→EXTRACT - 配合
GENERATED COLUMN或函数索引,能稳定落地优化方案 - 但别指望靠它“提速”——索引是否生效、数据分布、统计信息更新程度,才是关键
EXTRACT,如果源字段是 TIMESTAMP WITH TIME ZONE,结果仍按当前会话时区解析,不是 UTC,也不是存储时区。这点在跨时区服务中极易引发统计偏差。

















