DATE_PART('hour', timestamp)是PostgreSQL提取小时的正确写法,需加单引号且返回double precision,建议转int;对timestamptz按会话时区转换;直接函数过滤会导致索引失效,应改用时间范围查询。

DATE_PART('hour', timestamp) 是提取小时的正确写法
PostgreSQL 的 DATE_PART 函数必须显式指定字段名字符串和时间值,不能省略引号或写成 hour() 这类别名形式。最常见错误是写成 DATE_PART(hour, created_at)(漏掉单引号),这会报错:column "hour" does not exist。
它返回的是 double precision 类型,不是整数——即使你传入的是整点时间,结果也是 14.0 而非 14。如果后续要用于分组或比较,建议显式转成 int:
SELECT DATE_PART('hour', '2024-05-20 14:30:00'::timestamp)::int;
注意:DATE_PART 对 timestamp with time zone 会自动按当前会话时区转换后再提取,不是简单截取原始字符串。
用在 WHERE 或 GROUP BY 里要注意索引失效风险
直接在条件中写 DATE_PART('hour', created_at) = 14,哪怕 created_at 上有 B-tree 索引,PostgreSQL 也大概率无法使用该索引——因为函数调用破坏了索引列的原始有序性。
更高效的做法是改写为时间范围查询:
- 查“下午 2 点整点发生的记录” →
created_at >= '2024-05-20 14:00:00' AND created_at - 查“所有日期中下午 2 点的记录” → 用生成的时间范围配合
AND EXTRACT(HOUR FROM created_at) = 14并不比函数快,此时应考虑函数索引
若真要高频按小时过滤,建函数索引更稳妥:
CREATE INDEX idx_orders_hour ON orders (DATE_PART('hour', created_at)::int);
DATE_PART 和 EXTRACT 行为一致,但语法风格不同
EXTRACT(HOUR FROM timestamp) 和 DATE_PART('hour', timestamp) 在 PostgreSQL 中完全等价,返回值类型、时区处理、NULL 行为都一样。区别只在语法偏好:
-
EXTRACT是 SQL 标准写法,字段关键字不加引号,更易读 -
DATE_PART是 PostgreSQL 特有,字段名必须用单引号,但支持更多非标准字段(如'epoch'、'quarter')
二者性能无差异,选哪个取决于团队规范或是否需兼容其他数据库。不要混用——比如写成 DATE_PART('hour', EXTRACT(...)) 就纯属冗余。
跨天聚合时别忽略时区和夏令时影响
如果你按小时统计活跃用户,并且业务覆盖多个时区,仅用 DATE_PART('hour', created_at) 可能导致数据倾斜。例如 UTC+8 的凌晨 2 点,在 UTC 是前一天的 18 点,而某些地区夏令时切换当天可能没有 2 点或出现两个 2 点。
稳妥做法是统一转到业务主时区再提取:
SELECT DATE_PART('hour', created_at AT TIME ZONE 'Asia/Shanghai')::int AS hour_of_shanghai
注意:AT TIME ZONE 后面的时区名必须是 PostgreSQL 支持的完整名称(如 'America/New_York'),不能用缩写('EST' 不可靠)。
真正难处理的是历史数据中缺失时区信息的情况——这时 DATE_PART 提取的只是字面小时,不代表真实业务小时,得靠业务上下文补全。










