按小时分组应使用DATE_TRUNC(PostgreSQL等)或等效函数,避免多层日期函数导致跨天错误;WHERE条件须作用于原始时间字段以利用索引,如WHERE event_time >= '2024-05-20 09:00:00' AND event_time < '2024-05-20 18:00:00'。

用 DATE_TRUNC 或 DATEPART 按小时分组
PostgreSQL 和大多数现代数据库(如 ClickHouse、Trino)支持 DATE_TRUNC('hour', timestamp),它能把任意时间戳截断到所在小时的起始点(例如 '2024-05-20 14:37:22' → '2024-05-20 14:00:00'),这是做每小时聚合最直接的方式。MySQL 和 SQL Server 不支持 DATE_TRUNC,得换写法。
常见错误是用 YEAR()/HOUR() 多层嵌套拼时间,结果跨天或跨月时分组错乱(比如把 2024-01-01 23:59 和 2024-01-02 00:15 归到同一“小时”里)。必须保证分组键是完整可比的时间点,而不是离散数字。
- PostgreSQL / DuckDB / BigQuery:
GROUP BY DATE_TRUNC('hour', event_time) - MySQL 8.0+:
GROUP BY DATE_FORMAT(event_time, '%Y-%m-%d %H:00:00') - SQL Server:
GROUP BY DATEADD(HOUR, DATEDIFF(HOUR, 0, event_time), 0) - SQLite:
GROUP BY strftime('%Y-%m-%d %H:00:00', event_time)
WHERE 条件必须覆盖原始时间字段,不能作用于分组后的时间
如果写成 WHERE DATE_TRUNC('hour', event_time) BETWEEN '2024-05-20 09:00' AND '2024-05-20 17:00',虽然逻辑看似对,但会强制对每一行都计算函数,无法走 event_time 字段上的索引,查询可能极慢。正确做法是把范围条件落在原始时间列上。
- ✅ 正确(能命中索引):
WHERE event_time >= '2024-05-20 09:00:00' AND event_time - ❌ 错误(全表扫描风险高):
WHERE DATE_TRUNC('hour', event_time) >= '2024-05-20 09:00' AND ... - 注意右边界用
而不是 <code>,避免把 18:00:00 整点数据重复计入下一小时
SELECT 中的 AVG() 要明确处理 NULL 和空窗口
AVG() 会自动忽略 NULL 值,但如果某个小时完全没有数据,该小时就不会出现在结果里——这不是缺失值,而是压根没记录。业务上常需要补零(比如画折线图时不能缺小时)。SQL 本身不提供“生成时间序列”的能力,得靠外部补全或 LEFT JOIN 临时序列。
- 若只要已有数据的平均值,直接写
AVG(value)即可 - 若需强制输出每小时(含空值),PostgreSQL 可用
GENERATE_SERIES()构造时间桶再 LEFT JOIN;MySQL 需提前建小时维度表或用 UNION ALL 拼 24 行 - 警惕
AVG()在整数列上的隐式截断:MySQL 中AVG(int_col)默认返回DECIMAL(10,0),可能丢小数位,建议显式转成AVG(CAST(int_col AS DECIMAL(10,2)))
时区问题常被忽略,尤其日志时间存的是 UTC
数据库服务器时区、连接会话时区、业务要求的本地时区三者不一致时,DATE_TRUNC 或 DATE_FORMAT 的结果会偏移。例如服务器在 UTC,但你要查北京时间(UTC+8)的每小时均值,直接 trunc 会按 UTC 小时切分,导致 9:00–10:00 北京时间的数据被拆到两个 UTC 小时里。
- 推荐统一在应用层转换时间范围:把「查 5 月 20 日 9–17 点北京」转成 UTC 时间段
2024-05-20 01:00:00到2024-05-20 09:00:00,再传给 SQL - PostgreSQL 可用
AT TIME ZONE:DATE_TRUNC('hour', event_time AT TIME ZONE 'Asia/Shanghai'),但要注意这会影响索引使用 - 日志表若已存本地时间(如
'2024-05-20 14:30:00'且无时区标识),务必确认它到底代表哪个时区,否则所有聚合都不可信
真正麻烦的不是写对一句 SQL,而是确认时间字段的语义、存储格式和业务期望的切分逻辑是否对齐。哪怕函数写得再标准,源头时间解释错了,结果就是错的。

















