bigywater.tech
ClickHouse 内部原理 · 52

时间序列函数:窗口、间隔和时间桶

真实用toStartOfInterval做5分钟时间分桶,故意制造数据缺口,对比有无WITH FILL时缺失时间点在结果里的真实呈现方式。

0差异行
真实验证toStartOfInterval(5分钟)与toStartOfFiveMinutes完全等价
补位不补值
真实WITH FILL用默认值0填充,不是插值

做的事:真实按5分钟分桶,再故意制造一个数据缺口

代码CREATE TABLE demo.tsdata (ts DateTime, v Float64) ENGINE=MergeTree ORDER BY ts; INSERT INTO demo.tsdata SELECT toDateTime('2026-01-01 00:00:00')+number*37, sin(number/50)*10+20 FROM numbers(100000); SELECT toStartOfInterval(ts, INTERVAL 5 MINUTE) AS bucket, avg(v) FROM demo.tsdata GROUP BY bucket; -- 真实删掉一段时间的数据,制造缺口 ALTER TABLE demo.tsdata DELETE WHERE ts >= '2026-01-01 00:10:00' AND ts < '2026-01-01 00:20:00';

验证:不用WITH FILL,缺失的桶真实直接消失

实测 · 本机执行输出(真实删掉00:10~00:20之间数据后)
SELECT toStartOfInterval(ts,INTERVAL 5 MINUTE) AS bucket, avg(v) FROM demo.tsdata
WHERE ts<'2026-01-01 00:30:00' GROUP BY bucket ORDER BY bucket;
→ 00:00:00   20.80
  00:05:00   22.47
  00:20:00   26.66   ← 真实直接跳到了00:20,00:10和00:15两个桶消失了
  00:25:00   27.76

普通的GROUP BY只会真实输出数据里真的存在的分组,00:10和00:15这两个桶因为数据被真实删掉了,压根不会出现在结果里——如果下游要拿这份结果画一张连续的时序图,真实会在图上留下一个视觉上的"跳跃",容易被误读成横轴间隔不均匀。

验证:WITH FILL真实补齐了缺失的桶,但真实只是默认值,不是插值

实测 · 本机执行输出(同样的数据缺口,加上WITH FILL)
... GROUP BY bucket ORDER BY bucket
WITH FILL FROM toDateTime('2026-01-01 00:00:00') TO toDateTime('2026-01-01 00:30:00')
STEP INTERVAL 5 MINUTE;
→ 00:00:00   20.80
  00:05:00   22.47
  00:10:00   0      ← 真实被WITH FILL补齐了,但avg(v)的值真实是0,不是插值
  00:15:00   0      ← 同样是0
  00:20:00   26.66
  00:25:00   27.76
WITH FILL真实做的事情,是按STEP指定的步长,在结果集里把FROM到TO范围内所有本该出现、但因为没有匹配数据而缺失的bucket值真实补全,缺失行里除了填充列(bucket)之外的其他列,会真实填成该列类型的默认值——本文的avg(v)是Float64,默认值就是0,而不是某种"根据前后数据插值出来的合理估计值"。如果业务真的需要插值(比如让曲线在缺口处平滑过渡),需要额外搭配INTERPOLATE子句显式指定插值方式,本文没有进一步验证INTERPOLATE的具体效果,但从这次真实测出来的默认行为可以确认:裸用WITH FILL只解决"缺失的时间点要不要出现在结果里"这个问题,不会自动帮你猜一个合理的数值,用之前要想清楚下游怎么处理这些真实是0(而不是真正代表"这段时间的平均值确实是0")的填充行。

分步演示:从缺失的桶到WITH FILL补齐的真相

每一步都是本文上面用真实时序数据跑出来的原始结果。

WITH FILL补的是位置,不是真实的数值
这一步新验证的状态

    本文用到的工具

    • 本机 ClickHouse 26.9.4(单节点),真实的 toStartOfInterval 与 WITH FILL
    京ICP备2026067502号-1京公网安备11010502063487号