hour()函数从时间值中提取0–23的小时数,仅支持datetime、timestamp、time类型,对date返回0,字符串需先转换;与extract(hour from ...)功能一致但语法不同;where中直接使用会导致索引失效。

MySQL中HOUR()函数的基本用法
HOUR() 函数直接从时间或日期时间值中提取小时数(0–23),它不关心年月日,只看时分秒部分。传入 NULL 会返回 NULL,传入纯日期(如 '2024-05-10')则默认按 '00:00:00' 处理,结果恒为 0。
常见误用是拿它处理字符串但没转类型:
SELECT HOUR('2024-05-10 14:30:00'); -- ❌ 返回 NULL,因为不是合法 time/datetime 类型
正确做法是先用 STR_TO_DATE() 或隐式转换确保类型:
SELECT HOUR(STR_TO_DATE('2024-05-10 14:30:00', '%Y-%m-%d %H:%i:%s')); -- ✅ 返回 14
- 支持的输入类型:
DATETIME、TIMESTAMP、TIME - 不支持直接解析未格式化的字符串,哪怕看起来像时间
- 对
DATE类型字段调用HOUR()总是得0,这点容易被忽略
和EXTRACT(HOUR FROM ...)的区别在哪
HOUR() 和 EXTRACT(HOUR FROM ...) 功能一致,但语法风格和兼容性不同。前者是 MySQL 原生函数,后者符合 SQL 标准,在跨数据库迁移时更通用。
关键差异点:
-
HOUR()只接受单个表达式参数;EXTRACT()必须写成EXTRACT(HOUR FROM col),不能省略FROM -
EXTRACT()对非法时间容忍度更低——比如EXTRACT(HOUR FROM 'abc')直接报错Truncated incorrect datetime value,而HOUR('abc')返回NULL - 性能上无实质差别,执行计划几乎一致
- 如果字段是
TIMESTAMP且涉及时区,两者行为一致:都基于当前会话时区计算小时
WHERE条件里用HOUR()过滤时的性能陷阱
在 WHERE 子句中对时间字段套 HOUR()(如 WHERE HOUR(created_at) = 14)会导致全表扫描,因为无法使用索引——函数作用于列会破坏索引的有序性。
替代方案更高效:
- 改用范围查询:
WHERE created_at >= '2024-01-01 14:00:00' AND created_at - 若需查所有日期的14点,可建生成列 + 索引(MySQL 5.7+):
ALTER TABLE events ADD COLUMN hour_of_day TINYINT AS (HOUR(created_at)) STORED;<br>CREATE INDEX idx_hour ON events(hour_of_day);
- 避免在大表上对
HOUR()结果做GROUP BY,除非已加覆盖索引
处理时区偏移时HOUR()是否自动转换
不自动。只要存储类型是 TIMESTAMP,MySQL 内部以 UTC 保存,读取时按会话时区转换,HOUR() 拿到的是转换后的本地时间的小时;而 DATETIME 是“原样存、原样读”,HOUR() 返回的就是写入时的小时值,与时区无关。
验证方式:
SET time_zone = '+00:00'; SELECT HOUR('2024-05-10 14:00:00'); -- 返回 14<br>SET time_zone = '+08:00'; SELECT HOUR('2024-05-10 14:00:00'); -- 仍返回 14 —— 因为字符串不是 TIMESTAMP
真正受影响的是 TIMESTAMP 字段:
- 插入
INSERT INTO t(ts) VALUES ('2024-05-10 14:00:00')到TIMESTAMP列,在+08:00时区下实际存为2024-05-10 06:00:00 UTC - 之后无论会话时区怎么变,
HOUR(ts)都返回当前时区视角下的小时(如切到+00:00就显示6) - 所以业务中若依赖小时统计,务必确认字段类型和应用层时区设置是否匹配
最常被绕过的点:以为 HOUR() 能从任意格式字符串里“智能提取”,结果大量 NULL 不告而至;还有就是对 DATETIME 和 TIMESTAMP 在时区场景下的行为差异缺乏预判。











