extract(year from date) 并不比 year(date) 更高效,而是更符合sql标准、跨数据库兼容;但二者在where中对索引列使用均会导致b-tree索引失效,应改用范围查询如 order_time >= '2023-01-01' and order_time
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)::intEXTRACT('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,也不是存储时区。这点在跨时区服务中极易引发统计偏差。












