在对一个十分钟子查询进行聚合之后,为什么会有些设备组被过滤掉?

编程语言 2026-07-07

我在Apache IoTDB 2.0.8的表模型上运行一个告警摘要。该SQL先在子查询中把温度数据按10分钟分桶,然后外层查询统计穿越阈值的分桶数量。内部结果单独看起来还算合理,但外层的HAVING过滤掉的设备分组数量比我预期的要多。

模式与测试数据:

CREATE TABLE alarm_temperature_samples (time TIMESTAMP TIME, plant_id STRING TAG, device_id STRING TAG, temperature DOUBLE FIELD);
INSERT INTO alarm_temperature_samples(time, plant_id, device_id, temperature) VALUES ('2024-11-26 00:00:00', '1001', 'device_a', 85.0);
INSERT INTO alarm_temperature_samples(time, plant_id, device_id, temperature) VALUES ('2024-11-26 00:30:00', '1001', 'device_a', 72.0);
INSERT INTO alarm_temperature_samples(time, plant_id, device_id, temperature) VALUES ('2024-11-26 01:00:00', '1001', 'device_a', 72.0);
INSERT INTO alarm_temperature_samples(time, plant_id, device_id, temperature) VALUES ('2024-11-26 01:30:00', '3001', 'device_b', 72.0);
INSERT INTO alarm_temperature_samples(time, plant_id, device_id, temperature) VALUES ('2024-11-26 02:00:00', '3001', 'device_b', 72.0);
INSERT INTO alarm_temperature_samples(time, plant_id, device_id, temperature) VALUES ('2024-11-26 02:30:00', '3001', 'device_b', 72.0);

告警摘要所使用的嵌套聚合查询:

SELECT plant_id, device_id
FROM (
    SELECT date_bin(10m, time) AS time, plant_id, device_id, AVG(temperature) AS bucket_avg_temperature
    FROM alarm_temperature_samples 
    WHERE time >= 2024-11-26 00:00:00
    AND time <= 2024-11-29 00:00:00 
    GROUP BY 1, plant_id, device_id
)
WHERE bucket_avg_temperature > 80.0 
GROUP BY plant_id, device_id
HAVING COUNT(*) > 1;

运行结果:

plant_id device_id

单独检查的内部10分钟聚合:

SELECT *
FROM alarm_temperature_samples 
ORDER BY time LIMIT 5;

结果:

time plant_id device_id temperature
2024-11-26T00:00:00.000+08:00 1001 device_a 85.0
2024-11-26T00:30:00.000+08:00 1001 device_a 72.0
2024-11-26T01:00:00.000+08:00 1001 device_a 72.0
2024-11-26T01:30:00.000+08:00 3001 device_b 72.0
2024-11-26T02:00:00.000+08:00 3001 device_b 72.0

我想要确认的是:

外层的COUNT(*) 是统计由内部聚合产生的行,还是原始未聚合的行?如果我想表达同一设备在多个10分钟窗口内超过阈值,这个嵌套查询的结构是否正确?

解决方案

HAVING 子句是对聚合后分组的过滤。它与 WHERE 子句的区别恰恰在于 WHERE 子句是对单条记录的过滤,而 HAVING 子句是对聚合的过滤。因此你不能在 HAVING 子句中使用未聚合的字段,因为聚合已经发生后它就没有意义,但你可以使用你分组时的聚合字段,即你的:

  • plant_id
  • device_id

你也可以使用聚合函数,例如在你的场景中使用 COUNT(*)。现在已经清楚 HAVING 子句是对聚合进行过滤,并且你的聚合基于子查询生成的关系,

HAVING COUNT(*) > 1

这意味着你只对包含多于一条记录的分组感兴趣。考虑到你内部查询的

    SELECT date_bin(10m, time) AS time, plant_id, device_id, AVG(temperature) AS bucket_avg_temperature
    FROM alarm_temperature_samples 
    WHERE time >= 2024-11-26 00:00:00
    AND time <= 2024-11-29 00:00:00 
    GROUP BY 1, plant_id, device_id

基本上,你会生成一个关系,其中包含按分桶分组的记录,以及进入你指定时间区间的记录,它们对应的 plant_iddevice_id

然而,你的外层 WHERE 子句是在你生成的关系的每条记录上运行并检查

WHERE bucket_avg_temperature > 80.0

是否为真,该条件为真时保留符合条件的记录,否则全部过滤。因此,你的外层按记录的筛选表示没有任何记录符合条件,至少达到这个平均温度的记录才会通过筛选,因此只有平均温度为85.0的那条记录“存活”下来,从而你的 GROUP BY 将这条记录聚成一个只有该记录的分组,但该分组被过滤,因为它是由唯一存活的元素来分组。

外层COUNT(*) 是统计由内部聚合产生的行,还是原始的未聚合行?

COUNT(*) 取决于上下文。你的上下文是

GROUP BY plant_id, device_id
HAVING COUNT(*) > 1

所以基本上你生成了分组,只有在它们聚合自的记录数至少为两条时,才希望在结果集中看到这些分组。

如果我想表达同一设备在多个10分钟窗口内超过阈值,这个嵌套查询的结构正确吗?

是的。但你的输入没有这样的设备,因为在你的子查询中超过阈值的记录恰好只有一条。

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章