在处理大规模时序数据时,数据切分查询是一种重要的技术手段。TDengine时序数据库提供了PARTITION BY子句,支持按维度对数据进行切分,然后在每个切分空间内进行独立计算。本文将详细介绍数据切分查询的原理和应用。
一、数据切分查询概述
1.1 什么是数据切分查询
数据切分查询是指将数据按照一定维度进行分组,每个分组形成一个独立的数据空间,然后在这些空间内分别执行后续的计算操作。
1.2 数据切分的价值
- 并行计算:不同分片可以并行处理,提高查询效率
- 逻辑清晰:将复杂查询分解为多个独立计算单元
- 结果可控:可以限制返回的分片数量,控制结果规模
二、PARTITION BY子句
2.1 语法结构
PARTITION BY part_list
part_list可以是任意的标量表达式,包括:
- 列名
- 常量
- 标量函数
- 它们的组合
2.2 处理流程
TDengine按如下方式处理数据切分子句:
- 数据切分子句位于WHERE子句之后
- 数据切分子句将表数据按指定的维度进行切分,每个切分的分片进行指定的计算
- 计算由之后的子句定义(窗口子句、GROUP BY子句或SELECT子句)
- 数据切分子句可以和窗口切分子句(或GROUP BY子句)一起使用,此时后面的子句作用在每个切分的分片上
2.3 基本示例
按标签切分数据:
SELECT location, avg(voltage)
FROM meters
PARTITION BY location;
查询结果:
location | avg(voltage) |
======================================================
California.SantaClara | 243.962050000000005 |
California.SanFrancisco | 243.962050000000005 |
California.SanJose | 243.962050000000005 |
California.LosAngles | 243.962050000000005 |
California.SanDiego | 243.962050000000005 |
California.Sunnyvale | 243.962050000000005 |
California.PaloAlto | 243.962050000000005 |
California.Cupertino | 243.962050000000005 |
California.MountainView | 243.962050000000005 |
California.Campbell | 243.962050000000005 |
Query OK, 10 row(s) in set (2.415961s)
三、PARTITION BY与窗口查询结合
3.1 组合使用场景
当需要对每个设备分别进行时间窗口聚合时,可以将PARTITION BY与窗口子句结合使用。
3.2 完整示例
SELECT tbname, _wstart, _wend, avg(voltage)
FROM meters
WHERE ts >= "2022-01-01T00:00:00+08:00"
AND ts < "2022-01-01T00:05:00+08:00"
PARTITION BY tbname
INTERVAL(1m, 5s)
SLIMIT 2;
这条SQL的执行流程:
- WHERE过滤:筛选指定时间范围的数据
- PARTITION BY切分:按子表名(tbname)切分数据
- INTERVAL窗口:在每个分片内按1分钟窗口聚合
- SLIMIT限制:只返回前2个分片的结果
查询结果:
tbname | _wstart | _wend | avg(voltage) |
======================================================================================
d2 | 2021-12-31 23:59:05.000 | 2022-01-01 00:00:05.000 | 253.000000000000000 |
d2 | 2022-01-01 00:00:05.000 | 2022-01-01 00:01:05.000 | 244.166666666666657 |
d2 | 2022-01-01 00:01:05.000 | 2022-01-01 00:02:05.000 | 241.833333333333343 |
d2 | 2022-01-01 00:02:05.000 | 2022-01-01 00:03:05.000 | 243.166666666666657 |
d2 | 2022-01-01 00:03:05.000 | 2022-01-01 00:04:05.000 | 240.833333333333343 |
d2 | 2022-01-01 00:04:05.000 | 2022-01-01 00:05:05.000 | 244.800000000000011 |
d26 | 2021-12-31 23:59:05.000 | 2022-01-01 00:00:05.000 | 253.000000000000000 |
d26 | 2022-01-01 00:00:05.000 | 2022-01-01 00:01:05.000 | 244.166666666666657 |
d26 | 2022-01-01 00:01:05.000 | 2022-01-01 00:02:05.000 | 241.833333333333343 |
d26 | 2022-01-01 00:02:05.000 | 2022-01-01 00:03:05.000 | 243.166666666666657 |
d26 | 2022-01-01 00:03:05.000 | 2022-01-01 00:04:05.000 | 240.833333333333343 |
d26 | 2022-01-01 00:04:05.000 | 2022-01-01 00:05:05.000 | 244.800000000000011 |
Query OK, 12 row(s) in set (0.021265s)
四、SLIMIT与SOFFSET
4.1 SLIMIT子句
SLIMIT用于限制返回的分片数量:
-- 只返回前1个分片
SELECT tbname, avg(voltage)
FROM meters
PARTITION BY tbname
SLIMIT 1;
4.2 SOFFSET子句
SOFFSET用于指定跳过的分片数量:
-- 跳过前5个分片,返回接下来的3个分片
SELECT tbname, avg(voltage)
FROM meters
PARTITION BY tbname
SLIMIT 3 SOFFSET 5;
4.3 分页查询
结合SLIMIT和SOFFSET实现分片分页:
-- 第一页
SELECT tbname, avg(voltage)
FROM meters
PARTITION BY tbname
SLIMIT 10 SOFFSET 0;
-- 第二页
SELECT tbname, avg(voltage)
FROM meters
PARTITION BY tbname
SLIMIT 10 SOFFSET 10;
五、常用切分维度
5.1 按子表名切分
最常用的切分方式,按设备分片:
SELECT tbname, COUNT(*), AVG(voltage)
FROM meters
WHERE ts >= '2022-01-01 00:00:00'
PARTITION BY tbname;
5.2 按标签切分
按标签值进行分组:
SELECT location, COUNT(*), AVG(voltage)
FROM meters
WHERE ts >= '2022-01-01 00:00:00'
PARTITION BY location;
5.3 按表达式切分
支持使用表达式作为切分依据:
-- 按分组ID模3切分
SELECT group_id % 3 as grp, AVG(voltage)
FROM meters
PARTITION BY group_id % 3;
六、PARTITION BY与GROUP BY的区别
| 特性 | PARTITION BY | GROUP BY |
|---|---|---|
| 作用位置 | WHERE之后 | 窗口子句之后 |
| 与窗口子句 | 可以一起使用 | 不能一起使用 |
| 计算方式 | 每个分片独立计算 | 全局聚合 |
| 结果形式 | 多个分片的结果集 | 单一聚合结果 |
6.1 GROUP BY示例
SELECT groupid, avg(voltage)
FROM meters
WHERE ts >= "2022-01-01T00:00:00+08:00"
GROUP BY groupid;
6.2 PARTITION BY示例
SELECT tbname, avg(voltage)
FROM meters
WHERE ts >= "2022-01-01T00:00:00+08:00"
PARTITION BY tbname;
七、高级应用场景
7.1 设备级时间窗口分析
对每个设备分别进行时间窗口聚合:
SELECT tbname, _wstart, avg(voltage), max(voltage), min(voltage)
FROM meters
WHERE ts >= '2022-01-01 00:00:00' AND ts < '2022-01-02 00:00:00'
PARTITION BY tbname
INTERVAL(1h)
SLIMIT 10;
7.2 地区分组统计
按地区分组进行统计:
SELECT location,
COUNT(*) as device_count,
AVG(voltage) as avg_voltage,
MAX(voltage) as max_voltage
FROM meters
PARTITION BY location;
7.3 多维度切分
支持多表达式切分:
SELECT location, group_id, AVG(voltage)
FROM meters
PARTITION BY location, group_id;
八、性能优化建议
8.1 合理选择切分维度
- 选择区分度高的列作为切分维度
- 避免切分后分片数量过多
- 标签列是理想的切分维度
8.2 使用SLIMIT控制结果
- 大规模数据查询时使用SLIMIT限制分片数量
- 结合SOFFSET实现分页
- 避免一次性返回过多数据
8.3 配合时间范围过滤
- 在WHERE子句中指定时间范围
- 减少每个分片的数据量
- 提高查询效率
九、注意事项
9.1 子句顺序
正确的子句顺序:
SELECT ...
FROM ...
WHERE ...
PARTITION BY ...
[窗口子句]
[SLIMIT ...]
9.2 与GROUP BY的限制
- PARTITION BY不能与GROUP BY同时使用
- 根据需求选择合适的分组方式
9.3 结果排序
PARTITION BY不保证结果顺序,如需排序应使用ORDER BY:
SELECT tbname, avg(voltage)
FROM meters
PARTITION BY tbname
ORDER BY tbname;
总结
数据切分查询是TDengine时序数据库的重要特性,通过PARTITION BY子句,开发者可以灵活地按维度对数据进行切分,实现并行计算和精细化分析。结合窗口子句和SLIMIT限制,可以构建强大的时序数据分析查询。这种技术特别适用于物联网和工业场景,能够高效处理海量设备数据,为工业数据管理平台(IDMP)和实时数据库应用提供灵活的数据分析能力。TDengine凭借其完善的数据切分查询功能,成为大规模时序数据分析的理想选择。

























