MySQL不支持直接按15分钟动态分组,需用UNIX_TIMESTAMP转秒级时间戳,减基准时间(如当日0点),除以900后FLOOR取整再还原;时区未对齐是分组错位主因。

MySQL里用DATE_SUB和FLOOR做时间切片分组
直接说结论:SQL本身不支持“每15分钟一组”这种动态间隔分组,得靠日期函数+数学运算把时间映射成离散整数,再按整数分组。核心思路是把时间转成秒级时间戳,除以间隔秒数取整,再还原回时间起点。
常见错误是直接用DATE_FORMAT(dt, '%Y-%m-%d %H:%i')截断分钟,结果每小时只能切出00/15/30/45四个固定点,但实际数据可能跨天、跨时区,或需要从某一起始时间(比如当天0点)开始对齐——这时候DATE_FORMAT就失效了。
- 正确做法:用
UNIX_TIMESTAMP(dt)转为整数,减去基准时间戳(如当天0点),再除以间隔秒数(如15×60=900),最后FLOOR取整 - 基准时间要显式计算,别依赖
NOW()——否则凌晨跑批时会跨天错位 - MySQL 8.0+ 可用
DATE_TRUNC,但只支持年月日时分,不支持任意秒级间隔,仍需手动算
PostgreSQL用width_bucket和generate_series更直观
PostgreSQL原生支持区间分桶,比MySQL少绕弯。但要注意:width_bucket返回的是桶序号,不是时间值,想还原成时间范围得配合generate_series或自定义计算。
典型场景是画监控折线图,需要补全空时间段(比如某15分钟没数据,也要显示0)。这时不能只靠GROUP BY,得先生成完整时间序列,再LEFT JOIN原始数据。
-
width_bucket(EXTRACT(EPOCH FROM ts), 0, EXTRACT(EPOCH FROM NOW()), 96)—— 把当天秒数分成96段(24×4),但起始点是0秒,不是当天0点,需先减去DATE_PART('epoch', CURRENT_DATE) - 用
generate_series生成时间点时,务必指定timezone,否则UTC和本地时间混用会导致分组偏移 - 如果表里
ts是timestamptz,EXTRACT(EPOCH FROM ts)自动转UTC;如果是timestamp,则按服务器时区解释——这点极易踩坑
SQLite里没有UNIX_TIMESTAMP,得用julianday硬算
SQLite连标准时间函数都缺,julianday返回的是儒略日(带小数的浮点数),单位是天。想实现每30分钟分组,就得把差值换算成天的小数,再乘以(24×2)=48,最后CAST(... AS INTEGER)取整。
示例:按每30分钟分组统计,起始点为当天0点:
SELECT
datetime(julianday('now', 'start of day') + CAST((julianday(ts) - julianday('now', 'start of day')) * 48 AS INTEGER) / 48.0) AS interval_start,
COUNT(*)
FROM logs
GROUP BY interval_start;
这个写法看着绕,但没办法——SQLite没FLOOR,也没TRUNC,CAST(x AS INTEGER)对正数等价于向下取整,但对负数会向零截断,所以必须确保所有时间都在当天内(即ts >= 'now', 'start of day')。
- 如果数据跨多天,得先用
date(ts)分日,再在每日内算分组,否则julianday差值会溢出精度 -
julianday精度只有毫秒级,高频写入(如每秒千条)时,同一分钟内的多条记录可能被分到不同桶——这不是逻辑错,是浮点舍入导致的
所有数据库都绕不开的时区陷阱
时间分组错位,八成是因为时区没对齐。哪怕你代码里写了AT TIME ZONE 'Asia/Shanghai',底层存储类型是否匹配?查询时是否触发隐式转换?
- MySQL中
DATETIME类型不存时区,TIMESTAMP才存UTC——但GROUP BY时如果混用NOW()(返回TIMESTAMP)和dt(DATETIME),MySQL会按系统时区来回转,结果不可控 - PostgreSQL里
timestamptz转timestamp会丢时区信息,再拿去算EXTRACT,就等于默认按UTC处理,而你的业务可能按东八区对齐 - 最稳的做法:统一用UTC存,所有分组计算也基于UTC时间戳;展示层再转本地时区——别在SQL里做
AT TIME ZONE后分组,性能差还易错
真正麻烦的不是怎么写,而是查出分组结果后发现第一组时间是2024-05-01 00:13:00——说明基准时间没对齐,或者时区转换在某个环节悄悄发生了。这种问题不会报错,只会让数据看起来“差不多”,但关键时段统计偏差越来越大。










