HOUR()函数可直接提取小时并分组统计,MySQL和SQL Server支持该函数,PostgreSQL需用EXTRACT(HOUR FROM ...);跨天统计应拼接日期与小时生成区间标签,注意时区转换与NULL值处理,并避免函数导致索引失效。

用 HOUR() 提取小时并分组统计最直接
MySQL 和 SQL Server 都支持 HOUR() 函数,能从 DATETIME 或 TIMESTAMP 字段中快速提取小时值(0–23),配合 GROUP BY 即可完成按小时段聚合。PostgreSQL 则需改用 EXTRACT(HOUR FROM ...),语法略有差异但逻辑一致。
常见错误是误用 DATE() 或 YEARWEEK() 导致粒度变粗;也有用户把 TIME() 当作小时字段直接 GROUP BY,结果因秒/毫秒精度导致每条记录都分到不同组——实际应先截断或转换为整数小时。
-
HOUR(created_at)是 MySQL 最简方案,注意字段必须是时间类型,字符串需先用STR_TO_DATE()转换 - SQL Server 用
DATEPART(HOUR, created_at),别写成DATEPART(hh, ...)——虽然兼容,但可读性差且部分工具不识别缩写 - PostgreSQL 示例:
SELECT EXTRACT(HOUR FROM accessed_at) AS hour, COUNT(*) FROM logs GROUP BY hour ORDER BY hour
跨天统计时务必用 DATE_FORMAT() 或 CONCAT() 拼接日期+小时
只按小时分组会把所有日期的“14点”合并,丢失时间上下文。真实业务中通常需要“2024-05-20 14:00–14:59”这样的区间标签,而非单纯数字 14。
MySQL 推荐用 DATE_FORMAT(created_at, '%Y-%m-%d %H:00'),它把每条记录归入对应整点起始的小时段;SQL Server 可用 FORMAT(created_at, 'yyyy-MM-dd HH:00'),但要注意 FORMAT 性能较差,大数据量建议改用 CONVERT + 字符串拼接。
- 避免写
CONCAT(DATE(created_at), ' ', HOUR(created_at), ':00')——HOUR()返回整数,会导致 “2024-05-20 9:00” 这种缺零格式,影响排序和展示 - 如果字段是字符串(如
'2024/05/20 14:32:15'),先转时间再格式化:DATE_FORMAT(STR_TO_DATE(log_time, '%Y/%m/%d %H:%i:%s'), '%Y-%m-%d %H:00') - PostgreSQL 中用
TO_CHAR(accessed_at, 'YYYY-MM-DD HH24:00'),注意HH24表示24小时制,HH是12小时制
遇到 NULL 时间或时区错乱时,统计结果会大幅偏低
日志表里 created_at 为空很常见,GROUP BY 会自动过滤掉这些行,导致总量对不上。另外,数据库服务器时区与业务所在时区不一致(比如服务器用 UTC,而业务在东八区),HOUR() 提取的是服务器本地小时,不是用户真实访问小时。
- 加
WHERE created_at IS NOT NULL显式排除空值,同时单独查空值数量:SELECT COUNT(*) FROM logs WHERE created_at IS NULL - MySQL 中用
CONVERT_TZ(created_at, '+00:00', '+08:00')先转时区再取小时;SQL Server 用AT TIME ZONE(2016+ 版本) - 若无法修改数据库配置,至少在应用层写清楚统计依据的时区,比如报表标题注明“按 UTC+8 小时段统计”
大表扫描慢?给时间字段加索引但别只建单列索引
created_at 上建普通索引对 HOUR(created_at) 查询几乎无效——函数会使索引失效。真正有效的做法是:要么建生成列索引(MySQL 5.7+、PostgreSQL),要么用范围查询替代函数计算。
- MySQL 示例:添加生成列
hour_slot TINYINT AS (HOUR(created_at)) STORED,再对它建索引 - 更通用的写法是绕过函数:用
WHERE created_at >= '2024-05-20 14:00:00' AND created_at 配合 <code>created_at上的普通 B-tree 索引,效率更高 - 如果每天数据量超百万,考虑按天分区(
PARTITION BY RANGE (TO_DAYS(created_at))),避免全表扫描

















