用 MOD 和 FLOOR 实现任意分钟粒度分组最可靠,核心是将时间转为秒级整数、减基准时间后除以粒度再向下取整;需确保字段为 DATETIME/TIMESTAMP 类型,注意时区转换与基准时间选取。

用 MOD 和 FLOOR 实现任意分钟粒度分组
直接对时间戳做算术截断,是绕过 DATE_FORMAT 限制、支持任意分钟(如 7 分、25 分)分组的最可靠方式。核心思路:把时间转成秒级整数,减去基准时间后取模,再向下取整得到“第几段”。
常见错误是忽略时区和字段类型:如果 product_time 是字符串或带毫秒的 DATETIME(3),UNIX_TIMESTAMP() 会返回 NULL,整组数据被归入空桶。
- 必须确保字段是
DATETIME或TIMESTAMP类型;若为字符串,先用STR_TO_DATE(product_time, '%Y-%m-%d %H:%i:%s') - 基准时间(如
'2026-01-01 00:00:00')建议选早于你所有数据的整点时刻,避免负数导致FLOOR行为异常 - 10 分钟分组写法:
FLOOR((UNIX_TIMESTAMP(product_time) - UNIX_TIMESTAMP('2026-01-01 00:00:00')) / 600),其中600= 10 × 60 - 结果列可配合
DATE_ADD还原为可读时间:DATE_ADD('2026-01-01 00:00:00', INTERVAL time_group * 600 SECOND)
MOD 截断法比 DATE_FORMAT 更准的原因
DATE_FORMAT(product_time, '%Y-%m-%d %H:%i') 看似方便,但它只是字符串截断——比如 '2026-04-12 14:29:59' 和 '2026-04-12 14:30:00' 会被分到不同组,哪怕你想要的是“每30分钟一桶”。而 MOD + FLOOR 是按真实时间跨度切分,不依赖显示格式。
性能上也更稳:DATE_FORMAT 无法利用时间字段上的索引,而 UNIX_TIMESTAMP() 虽然也是函数,但 MySQL 对其有优化;若你建了生成列(如 ts_sec INT AS (UNIX_TIMESTAMP(product_time)) STORED)并加索引,查询能明显提速。
- 别在
WHERE里对DATE_FORMAT结果过滤,应改用时间范围:product_time BETWEEN '2026-04-12 00:00:00' AND '2026-04-12 23:59:59' - 用
MOD截断时,UNIX_TIMESTAMP()返回的是 UTC 秒数,如果你的业务时间是东八区,需先转换:UNIX_TIMESTAMP(CONVERT_TZ(product_time, '+00:00', '+08:00'))
为什么不能只用 MOD,必须套 FLOOR
MOD 本身是取余操作,它给出的是“落在当前区间内的偏移量”,不是“属于第几个区间”。例如,想每15分钟分一组,MOD(UNIX_TIMESTAMP(t), 900) 返回的是 0~899 的余数,完全没法分组。
真正需要的是商(即第几段),所以必须用 FLOOR((x - base) / interval)。注意这里必须显式减去基准时间,否则跨天时会出现大负数,FLOOR(-1.2) 得到 -2,导致分组错位。
- 错误写法:
FLOOR(UNIX_TIMESTAMP(product_time) / 900)—— 从 Unix 元年起算,第 0 组是 1970 年,根本不可读 - 正确写法:
FLOOR((UNIX_TIMESTAMP(product_time) - UNIX_TIMESTAMP('2026-04-01 00:00:00')) / 900) - 如果基准时间不确定,可用
MIN(product_time)动态计算,但需子查询或 CTE,增加复杂度
按 15 分钟分组的实际 SQL 示例
以下语句统计表 events 中每 15 分钟的记录数,并还原为起始时间字符串:
slot_start










