EXTRACT(YEAR FROM date)是最直接写法,支持DATE/TIMESTAMP/TIMESTAMPTZ类型,需显式转换字符串;返回numeric型,比较时建议显式转INTEGER;时区影响TIMESTAMPTZ结果,DATE无影响。

EXTRACT(YEAR FROM date) 是最直接的写法
PostgreSQL 的 EXTRACT 函数支持从时间类型中抽取指定字段,年份用 YEAR 关键字,大小写不敏感,但推荐大写保持可读性。它接受 DATE、TIMESTAMP、TIMESTAMPTZ 类型参数,不支持字符串——如果字段是 TEXT 或 VARCHAR,必须先显式转换。
常见错误现象:ERROR: function extract(unknown, text) does not exist,就是因传入了未转换的字符串。
-
SELECT EXTRACT(YEAR FROM '2023-05-12'::DATE);✅ 正确:显式类型转换 -
SELECT EXTRACT(YEAR FROM '2023-05-12');❌ 报错:字符串无法隐式匹配函数签名 -
SELECT EXTRACT(YEAR FROM created_at) FROM orders;✅ 假设created_at是TIMESTAMP列
EXTRACT 返回的是 numeric 类型,不是 integer
这是容易被忽略的细节:EXTRACT 所有返回值都是 numeric(任意精度小数),哪怕你抽的是年份。这意味着它不能直接用于需要 integer 的上下文,比如作为 IN 子句中的整数字面量、或与 integer 列做等值比较时可能触发隐式类型转换警告(尤其在严格模式下)。
- 想转成整数?加
::INTEGER或CAST(... AS INTEGER),例如:EXTRACT(YEAR FROM now())::INTEGER - 和
integer列比较没问题(PostgreSQL 会自动 cast),但显式转换更清晰、避免歧义 - 在 GROUP BY 或 ORDER BY 中直接用
EXTRACT(YEAR FROM ...)没问题,因为排序/分组不依赖具体整数类型
替代方案:date_part() 和 DATE_PART() 效果相同
date_part() 是 EXTRACT 的别名函数,行为完全一致,参数顺序相反:date_part('year', timestamp)。它更接近某些其他数据库的习惯(如 PostgreSQL 自己文档里也常混用),但语义上不如 EXTRACT 直观。
-
EXTRACT(YEAR FROM t.ts)和date_part('year', t.ts)返回结果完全一样 - 注意引号:第一个参数是字符串字面量,必须用单引号,且内容小写(
'year'),而EXTRACT用关键字(无引号、大写) - 性能无差异,二者编译后执行计划一致;选哪个纯看团队风格或 SQL 可读性偏好
跨年计算或边界场景要小心时区
当字段是 TIMESTAMPTZ(带时区的时间戳)时,EXTRACT(YEAR FROM ...) 按当前会话时区解析年份。例如,UTC 时间 2024-01-01 00:00:00+00 在 Asia/Shanghai(UTC+8)会话中仍是 2024 年;但 2023-12-31 16:00:00+00 在上海时区是 2024-01-01 00:00:00 CST,提取结果就是 2024。
- 若需按 UTC 年份统一处理,先用
ts AT TIME ZONE 'UTC'转换再提取:EXTRACT(YEAR FROM (created_at AT TIME ZONE 'UTC')) - 若字段本就是
DATE类型,则完全不受时区影响——DATE本身不存时区信息 - 线上业务涉及跨时区统计(如全球订单按本地年份归档),务必确认时区上下文是否符合业务预期

















