date_trunc('hour', timestamp) 是最直接的按小时分组方式,将时间戳截断至小时起点,需在 select 和 group by 中重复书写完整表达式,不支持别名或 'hh' 等非标准单位,且 where 中使用可能使索引失效。

DATE_TRUNC('hour', timestamp) 是最直接的按小时分组方式
PostgreSQL 的 DATE_TRUNC 函数能将时间戳向下截断到指定精度,'hour' 是其标准精度参数之一。它不会四舍五入,而是“砍掉”分钟、秒、微秒部分,统一归到该小时的起始时刻(例如 '2024-05-20 14:37:22' → '2024-05-20 14:00:00')。
实际聚合时需配合 GROUP BY 和聚合函数使用:
SELECT
DATE_TRUNC('hour', created_at) AS hour_bucket,
COUNT(*) AS event_count
FROM logs
GROUP BY DATE_TRUNC('hour', created_at)
ORDER BY hour_bucket;
- 必须在
SELECT和GROUP BY中写完全相同的DATE_TRUNC('hour', ...)表达式,否则报错column "xxx" must appear in the GROUP BY clause - 别名(如
hour_bucket)不能直接用于GROUP BY—— PostgreSQL 不支持用别名重写分组表达式 - 如果
created_at是timestamp with time zone,DATE_TRUNC默认按数据库时区处理;跨时区统计时需先用AT TIME ZONE转换,例如DATE_TRUNC('hour', created_at AT TIME ZONE 'Asia/Shanghai')
注意 'hour' 和 'hh' 的区别:PostgreSQL 只认 'hour',不支持 'hh'
有人从 Oracle 或 SQL Server 迁移过来,习惯写 DATE_TRUNC('hh', ...),这在 PostgreSQL 中会直接报错:ERROR: invalid value for date_trunc: "hh"。PostgreSQL 仅接受标准单位字符串,包括 'microsecond'、'millisecond'、'second'、'minute'、'hour'、'day'、'week'、'month'、'quarter'、'year' 等。
PostgreSQL 18.4 官方 Ubuntu 安装包现已发布,这是目前最新的稳定版本。推荐通过官方 APT 仓库安装:先执行 sudo apt update 更新索引,再运行 sudo apt install postgresql-18 即可完成部署。新版本引入了异步 I/O 子系统,在顺序扫描与 VACUUM 场景下性能提升显著,同时支持 UUID v7 原生生成函数与虚拟生成列。
- 写成
'hh'、'HOUR'(大小写敏感)、'hours'(复数)都会失败 - 单位字符串必须是单引号包裹的字面量,不能是变量或拼接结果
- 若需动态精度(比如按参数切换小时/天),得用条件逻辑拼接 SQL,
DATE_TRUNC本身不支持变量精度
WHERE 条件中用 DATE_TRUNC 可能导致索引失效
如果表上有 created_at 字段的 B-tree 索引,但查询里写 WHERE DATE_TRUNC('hour', created_at) = '2024-05-20 14:00:00',PostgreSQL 通常无法高效利用该索引,因为函数调用改变了原始列值。
更高效的方式是改用范围查询:
WHERE created_at >= '2024-05-20 14:00:00' AND created_at
- 这种写法能命中
created_at上的原生索引,尤其对大表效果显著 - 注意右边界用
而非 <code>,避免漏掉恰好等于 <code>'2024-05-20 15:00:00'的记录 - 若业务常查某小时数据,可考虑建函数索引:
CREATE INDEX idx_logs_hour ON logs (DATE_TRUNC('hour', created_at));,但会增加写开销和存储
聚合结果的时间显示格式容易被误解
DATE_TRUNC('hour', ...) 返回的是 timestamp without time zone 或 timestamp with time zone 类型,取决于输入。默认输出格式(如 2024-05-20 14:00:00)看起来像整点,但本质仍是时间戳,不是字符串。
- 如果前端或报表工具把它当字符串截取(比如取前13位),可能出错;应始终用类型安全的方式处理
- 需要固定格式展示时,用
TO_CHAR(DATE_TRUNC('hour', created_at), 'YYYY-MM-DD HH24:00')更可靠,但注意这已脱离原始类型,不宜再用于计算或比较 - 跨日聚合(如凌晨时段)要注意时区转换是否引入日期偏移——例如 UTC 时间
'2024-05-20 16:00:00+00'在上海时区是'2024-05-21 00:00:00+08',DATE_TRUNC结果会按目标时区对齐
DATE_TRUNC,而应检查 WHERE 是否绕过了索引,以及时间字段的时区定义是否和业务预期一致。










