extract(year from date)是最直接的年份提取方法,返回整数型结果,适用于比较、分组和计算,但需注意关键字year不可加引号、源字段须为时间类型、null值返回null,且函数调用会阻碍索引使用。

EXTRACT(YEAR FROM date) 是最直接的写法
PostgreSQL 的 EXTRACT 函数专为从时间类型中拆解特定部分设计,提取年份时语法固定:EXTRACT(YEAR FROM column_name)。它不依赖格式化字符串或正则,也不受 datestyle 设置影响,结果是整数类型,可直接参与比较、分组或计算。
常见错误包括误写成 EXTRACT('year' FROM ...)(单引号导致解析为字符串字面量而非字段标识符),或混淆为 DATE_PART('year', ...)(虽功能等价但语法不同,且 DATE_PART 第一个参数必须加单引号)。
-
EXTRACT的第一个参数(如YEAR)是关键字,**不能加引号**;而DATE_PART的第一个参数是字符串,**必须加单引号** - 源字段类型需为
DATE、TIMESTAMP或TIMESTAMP WITH TIME ZONE;若为文本,须先用TO_DATE()或::DATE转换,否则报错ERROR: function extract(unknown, unknown) does not exist - 对
NULL值返回NULL,不是 0 或空字符串
在 WHERE 和 GROUP BY 中使用 EXTRACT 提取年份
实际查询中,常需按年份过滤或聚合。注意:不能在 WHERE 子句里直接写 EXTRACT(YEAR FROM order_date) = 2023 并期望走索引——因为这是函数调用,会阻止对 order_date 列的普通 B-tree 索引生效。
更高效的做法是改用范围查询:
WHERE order_date >= '2023-01-01'::DATE AND order_date <p>如果确实需要按年分组统计,<code>GROUP BY EXTRACT(YEAR FROM order_date)</code> 是合法且常用的方式,但要注意:该表达式必须出现在 <code>SELECT</code> 列表中(除非使用 PostgreSQL 10+ 的“隐式分组列”规则),否则报错 <code>column "..." must appear in the GROUP BY clause</code>。</p>
- 若同时要显示年份和计数,建议显式写出:
SELECT EXTRACT(YEAR FROM order_date) AS year, COUNT(*) FROM orders GROUP BY year - 避免在大表上频繁用
EXTRACT计算后过滤,优先考虑生成年份列或使用分区表
EXTRACT 和 TO_CHAR 的结果类型与用途差异
EXTRACT(YEAR FROM ...) 返回 DOUBLE PRECISION 类型(实际值为整数,如 2023.0),多数场景下可直接当整数用;而 TO_CHAR(order_date, 'YYYY') 返回 TEXT,适合拼接报表标题或导出文件名。
关键区别在于后续操作:你不能对 TO_CHAR 结果做数学运算(如 + 1),也不能安全地用于数值比较(比如 > '2022' 在某些 collation 下可能出错);而 EXTRACT 结果可直接参与 ORDER BY、窗口函数或子查询中的算术逻辑。
- 需要排序或计算 → 用
EXTRACT - 需要固定宽度字符串(如 '0023')、带前导零、或嵌入其他文本 → 用
TO_CHAR -
EXTRACT不支持自定义格式(如补零),也不处理本地化(如中文年份),这点和TO_CHAR有本质不同
时区对 EXTRACT(YEAR FROM timestamptz) 的影响
当输入是 TIMESTAMP WITH TIME ZONE(即 timestamptz)时,EXTRACT(YEAR FROM ...) 总是基于该时间戳在**当前会话时区**解释后的年份。例如,一条记录存储为 '2023-01-01 00:00:00+00',在 Asia/Shanghai 时区下执行 EXTRACT,会先转换为 '2023-01-01 08:00:00+08',年份仍是 2023;但如果时间是 '2022-12-31 16:00:00+00',在上海时区就变成 '2023-01-01 00:00:00+08',此时 EXTRACT 返回 2023 —— 这容易引发跨年统计偏差。
- 若业务逻辑要求按 UTC 年份统计,应先用
order_time AT TIME ZONE 'UTC'转换,再EXTRACT - 检查当前会话时区:运行
SHOW timezone; - 临时切换会话时区:执行
SET timezone = 'UTC';,但注意这会影响所有时间函数,不只是EXTRACT











