EXTRACT(YEAR FROM date) 是最直接的年份提取方式,返回整数、支持多种时间类型、对 NULL 返回 NULL,但不能用于 INTERVAL 且 WHERE 中使用会导致索引失效。

EXTRACT(YEAR FROM date) 是最直接的年份提取方式
PostgreSQL 的 EXTRACT 函数专为从时间值中拆解特定字段设计,提取年份时语法清晰、语义明确,比字符串截取或类型转换更可靠。
-
EXTRACT(YEAR FROM '2023-04-15'::DATE)返回整数2023,不是字符串 - 支持所有时间类型:
DATE、TIMESTAMP、TIMESTAMP WITH TIME ZONE - 对 NULL 输入返回 NULL,无需额外判空(但业务逻辑中仍需注意)
- 不能用于
INTERVAL类型——若误传会报错ERROR: cannot extract year from a type interval
WHERE 条件中用 EXTRACT(YEAR FROM col) 需警惕索引失效
直接在 WHERE 子句里写 EXTRACT(YEAR FROM created_at) = 2022 会导致全表扫描,即使 created_at 上有 B-tree 索引也用不上。
- 正确做法是改用范围查询:
created_at >= '2022-01-01' AND created_at - 若必须用年份字段做高频过滤,可建函数索引:
CREATE INDEX idx_orders_year ON orders (EXTRACT(YEAR FROM order_date)) - 注意函数索引只在查询中使用完全相同的表达式时才生效,比如
EXTRACT(YEAR FROM order_date) = 2022可命中,但EXTRACT(YEAR FROM order_date + INTERVAL '1 day') = 2022不行
和 date_part()、date_trunc() 的关键区别在哪
这三个函数常被混淆,但用途和返回值类型不同:
-
EXTRACT(YEAR FROM t)和date_part('year', t)行为一致,都返回double precision类型数字(实际值是整数,但类型是浮点),可用于数学运算 -
date_trunc('year', t)返回的是一个TIMESTAMP(如'2022-01-01 00:00:00'),适合做时间范围对齐或分组基准 - 不要用
date_trunc('year', t)::TEXT再截取年份——多此一举且易出错,不如直接EXTRACT
处理带时区的时间戳时,年份取决于本地时间还是 UTC
EXTRACT 基于输入值的「已解析时间」计算,不主动做时区转换。也就是说:
-
EXTRACT(YEAR FROM '2023-01-01 01:00:00+08'::TIMESTAMPTZ)→2023(按 +08 解析后是 2023 年) -
EXTRACT(YEAR FROM '2023-01-01 01:00:00+08'::TIMESTAMPTZ AT TIME ZONE 'UTC')→ 先转成 UTC 时间(即 2022-12-31 17:00:00),再提取 →2022 - 如果业务要求“按 UTC 年份统计”,必须显式加
AT TIME ZONE 'UTC',否则默认按存储时区或 session timezone 解析
时区上下文和索引优化这两点最容易被跳过,但线上查慢或结果偏差往往就出在这儿。










