mysql用hour()提取小时分组仅返回0–23整数,跨天数据会混淆;需配合where限定时间范围,大表须为时间字段建索引以防全表扫描。

MySQL里用 HOUR() 提取小时再分组统计
直接对时间字段用 HOUR() 函数提取小时数,配合 GROUP BY 就能按小时聚合。注意它只返回 0–23 的整数,不带日期信息,所以跨天数据会混在一起。
常见错误是把 NOW() 或 CURDATE() 当成原始时间字段来用,结果统计的是当前时刻,不是历史调用记录。
- 确保时间字段类型是
DATETIME或TIMESTAMP,CHAR类型需先用STR_TO_DATE()转换 - 如果要限定最近24小时,WHERE 条件写
WHERE api_time >= DATE_SUB(NOW(), INTERVAL 1 DAY) - 想同时看日期和小时?改用
DATE(api_time)和HOUR(api_time)联合分组
PostgreSQL 怎么写等效的按小时统计
PostgreSQL 没有 HOUR(),得用 EXTRACT(HOUR FROM api_time)。它返回 numeric 类型,但分组时自动转为整数,不影响使用。
容易踩的坑是忘记加 TIME ZONE 处理——如果数据库时区和业务时区不一致,比如服务器在 UTC、业务在东八区,EXTRACT 会按 UTC 算,导致小时偏移 8 小时。
- 强制转时区:写成
EXTRACT(HOUR FROM api_time AT TIME ZONE 'Asia/Shanghai') - 避免用
TO_CHAR(api_time, 'HH24')—— 它返回文本,排序或后续计算不方便 - 如果表很大,记得给
api_time字段建索引,否则全表扫描太慢
SQL Server 中 DATEPART() 的正确用法
DATEPART(HOUR, api_time) 是标准写法,但要注意它的行为和 MySQL 的 HOUR() 一致,只取小时部分,不考虑日期上下文。
一个典型问题是:用户想查“今天每小时调用量”,却只写了 GROUP BY DATEPART(HOUR, api_time),没加 WHERE CAST(api_time AS DATE) = GETDATE(),结果把所有历史数据都按小时叠起来了。
- 日期范围必须显式限定,SQL Server 不像 MySQL 那样支持
INTERVAL语法,用DATEADD构造范围更可靠 - 如果时间字段含毫秒,
DATEPART不受影响;但若用CONVERT(VARCHAR, api_time, 108)截取时间字符串,精度可能丢失 - 在 SQL Server 2016+ 上,可以用
FORMAT(api_time, 'yyyy-MM-dd HH')生成小时粒度时间戳,便于后续关联其他表
跨天统计时为什么不能只靠 HOUR()
单纯用 HOUR() 或等效函数,本质是做模 24 运算。凌晨 1 点和第二天凌晨 1 点都会变成 1,无法区分。这是绝大多数人一开始忽略的关键点。
真正生产环境需要的是“每小时时间段”,比如 “2024-05-20 14:00:00 ~ 2024-05-20 14:59:59” 这样的区间,而不是单个数字。
- 安全做法是先截断到小时:MySQL 用
DATE_FORMAT(api_time, '%Y-%m-%d %H:00:00'),PostgreSQL 用date_trunc('hour', api_time) - SQL Server 可以用
DATEADD(HOUR, DATEDIFF(HOUR, 0, api_time), 0)实现相同效果 - 截断后的时间戳可作为分组键,也能用于排序、窗口函数、和外部时间维度表 JOIN
WHERE HOUR(api_time) = 14),就会让索引失效。这点比怎么写 GROUP BY 更关键。











