数据操作语言(DML)

数据写入

IoTDB 为用户提供多种插入实时数据的方式,例如在 Cli/Shell 工具 中直接输入插入数据的 INSERT 语句,或使用 Java API(标准 Java JDBC 接口)单条或批量执行插入数据的 INSERT 语句。

本节主要为您介绍实时数据接入的 INSERT 语句在场景中的实际使用示例,有关 INSERT SQL 语句的详细语法请参见本文 INSERT 语句 节。

使用 INSERT 语句

使用 INSERT 语句可以向指定的已经创建的一条或多条时间序列中插入数据。对于每一条数据,均由一个时间戳类型的时间戳和一个数值或布尔值、字符串类型的传感器采集值组成。

在本节的场景实例下,以其中的两个时间序列root.ln.wf02.wt02.statusroot.ln.wf02.wt02.hardware为例 ,它们的数据类型分别为 BOOLEAN 和 TEXT。

单列数据插入示例代码如下:

IoTDB > insert into root.ln.wf02.wt02(timestamp,status) values(1,true)
IoTDB > insert into root.ln.wf02.wt02(timestamp,hardware) values(1, 'v1')

以上示例代码将长整型的 timestamp 以及值为 true 的数据插入到时间序列root.ln.wf02.wt02.status中和将长整型的 timestamp 以及值为”v1”的数据插入到时间序列root.ln.wf02.wt02.hardware中。执行成功后会返回执行时间,代表数据插入已完成。

注意:在 IoTDB 中,TEXT 类型的数据单双引号都可以来表示,上面的插入语句是用的是双引号表示 TEXT 类型数据,下面的示例将使用单引号表示 TEXT 类型数据。

INSERT 语句还可以支持在同一个时间点下多列数据的插入,同时向 2 时间点插入上述两个时间序列的值,多列数据插入示例代码如下:

IoTDB > insert into root.ln.wf02.wt02(timestamp, status, hardware) values (2, false, 'v2')

此外,INSERT 语句支持一次性插入多行数据,同时向 2 个不同时间点插入上述时间序列的值,示例代码如下:

IoTDB > insert into root.ln.wf02.wt02(timestamp, status, hardware) VALUES (3, false, 'v3'),(4, true, 'v4')

插入数据后我们可以使用 SELECT 语句简单查询已插入的数据。

IoTDB > select * from root.ln.wf02 where time < 5

结果如图所示。由查询结果可以看出,单列、多列数据的插入操作正确执行。

+-----------------------------+--------------------------+------------------------+
|                         Time|root.ln.wf02.wt02.hardware|root.ln.wf02.wt02.status|
+-----------------------------+--------------------------+------------------------+
|1970-01-01T08:00:00.001+08:00|                        v1|                    true|
|1970-01-01T08:00:00.002+08:00|                        v2|                   false|
|1970-01-01T08:00:00.003+08:00|                        v3|                   false|
|1970-01-01T08:00:00.004+08:00|                        v4|                    true|
+-----------------------------+--------------------------+------------------------+
Total line number = 4
It costs 0.170s

数据查询

时间条件过滤查询

本节主要介绍时间切片查询的相关示例,主要使用的是 IoTDB SELECT 语句。同时,您也可以使用 Java JDBC 标准接口来执行相关的查询语句。

根据一个时间区间选择一列数据

SQL 语句为:

select temperature from root.ln.wf01.wt01 where time < 2017-11-01T00:08:00.000

其含义为:

被选择的设备为 ln 集团 wf01 子站 wt01 设备;被选择的时间序列为温度传感器(temperature);该语句要求选择出该设备在“2017-11-01T00:08:00.000”(此处可以使用多种时间格式,详情可参看 2.1 节)时间点以前的所有温度传感器的值。

该 SQL 语句的执行结果如下:

+-----------------------------+-----------------------------+
|                         Time|root.ln.wf01.wt01.temperature|
+-----------------------------+-----------------------------+
|2017-11-01T00:00:00.000+08:00|                        25.96|
|2017-11-01T00:01:00.000+08:00|                        24.36|
|2017-11-01T00:02:00.000+08:00|                        20.09|
|2017-11-01T00:03:00.000+08:00|                        20.18|
|2017-11-01T00:04:00.000+08:00|                        21.13|
|2017-11-01T00:05:00.000+08:00|                        22.72|
|2017-11-01T00:06:00.000+08:00|                        20.71|
|2017-11-01T00:07:00.000+08:00|                        21.45|
+-----------------------------+-----------------------------+
Total line number = 8
It costs 0.026s

根据一个时间区间选择多列数据

SQL 语句为:

select status, temperature from root.ln.wf01.wt01 where time > 2017-11-01T00:05:00.000 and time < 2017-11-01T00:12:00.000;

其含义为:

被选择的设备为 ln 集团 wf01 子站 wt01 设备;被选择的时间序列为供电状态(status)和温度传感器(temperature);该语句要求选择出“2017-11-01T00:05:00.000”至“2017-11-01T00:12:00.000”之间的所选时间序列的值。

该 SQL 语句的执行结果如下:

+-----------------------------+------------------------+-----------------------------+
|                         Time|root.ln.wf01.wt01.status|root.ln.wf01.wt01.temperature|
+-----------------------------+------------------------+-----------------------------+
|2017-11-01T00:06:00.000+08:00|                   false|                        20.71|
|2017-11-01T00:07:00.000+08:00|                   false|                        21.45|
|2017-11-01T00:08:00.000+08:00|                   false|                        22.58|
|2017-11-01T00:09:00.000+08:00|                   false|                        20.98|
|2017-11-01T00:10:00.000+08:00|                    true|                        25.52|
|2017-11-01T00:11:00.000+08:00|                   false|                        22.91|
+-----------------------------+------------------------+-----------------------------+
Total line number = 6
It costs 0.018s

按照多个时间区间选择同一设备的多列数据

IoTDB 支持在一次查询中指定多个时间区间条件,用户可以根据需求随意组合时间区间条件。例如,

SQL 语句为:

select status,temperature from root.ln.wf01.wt01 where (time > 2017-11-01T00:05:00.000 and time < 2017-11-01T00:12:00.000) or (time >= 2017-11-01T16:35:00.000 and time <= 2017-11-01T16:37:00.000);

其含义为:

被选择的设备为 ln 集团 wf01 子站 wt01 设备;被选择的时间序列为“供电状态(status)”和“温度传感器(temperature)”;该语句指定了两个不同的时间区间,分别为“2017-11-01T00:05:00.000 至 2017-11-01T00:12:00.000”和“2017-11-01T16:35:00.000 至 2017-11-01T16:37:00.000”;该语句要求选择出满足任一时间区间的被选时间序列的值。

该 SQL 语句的执行结果如下:

+-----------------------------+------------------------+-----------------------------+
|                         Time|root.ln.wf01.wt01.status|root.ln.wf01.wt01.temperature|
+-----------------------------+------------------------+-----------------------------+
|2017-11-01T00:06:00.000+08:00|                   false|                        20.71|
|2017-11-01T00:07:00.000+08:00|                   false|                        21.45|
|2017-11-01T00:08:00.000+08:00|                   false|                        22.58|
|2017-11-01T00:09:00.000+08:00|                   false|                        20.98|
|2017-11-01T00:10:00.000+08:00|                    true|                        25.52|
|2017-11-01T00:11:00.000+08:00|                   false|                        22.91|
|2017-11-01T16:35:00.000+08:00|                    true|                        23.44|
|2017-11-01T16:36:00.000+08:00|                   false|                        21.98|
|2017-11-01T16:37:00.000+08:00|                   false|                        21.93|
+-----------------------------+------------------------+-----------------------------+
Total line number = 9
It costs 0.018s

按照多个时间区间选择不同设备的多列数据

该系统支持在一次查询中选择任意列的数据,也就是说,被选择的列可以来源于不同的设备。例如,SQL 语句为:

select wf01.wt01.status,wf02.wt02.hardware from root.ln where (time > 2017-11-01T00:05:00.000 and time < 2017-11-01T00:12:00.000) or (time >= 2017-11-01T16:35:00.000 and time <= 2017-11-01T16:37:00.000);

其含义为:

被选择的时间序列为“ln 集团 wf01 子站 wt01 设备的供电状态”以及“ln 集团 wf02 子站 wt02 设备的硬件版本”;该语句指定了两个时间区间,分别为“2017-11-01T00:05:00.000 至 2017-11-01T00:12:00.000”和“2017-11-01T16:35:00.000 至 2017-11-01T16:37:00.000”;该语句要求选择出满足任意时间区间的被选时间序列的值。

该 SQL 语句的执行结果如下:

+-----------------------------+------------------------+--------------------------+
|                         Time|root.ln.wf01.wt01.status|root.ln.wf02.wt02.hardware|
+-----------------------------+------------------------+--------------------------+
|2017-11-01T00:06:00.000+08:00|                   false|                        v1|
|2017-11-01T00:07:00.000+08:00|                   false|                        v1|
|2017-11-01T00:08:00.000+08:00|                   false|                        v1|
|2017-11-01T00:09:00.000+08:00|                   false|                        v1|
|2017-11-01T00:10:00.000+08:00|                    true|                        v2|
|2017-11-01T00:11:00.000+08:00|                   false|                        v1|
|2017-11-01T16:35:00.000+08:00|                    true|                        v2|
|2017-11-01T16:36:00.000+08:00|                   false|                        v1|
|2017-11-01T16:37:00.000+08:00|                   false|                        v1|
+-----------------------------+------------------------+--------------------------+
Total line number = 9
It costs 0.014s

根据时间降序返回结果集

IoTDB 在 0.11 版本开始支持 ‘order by time’ 语句,用于对结果按照时间进行降序展示。例如,SQL 语句为:

select * from root.ln.** where time > 1 order by time desc limit 10;

语句执行的结果为:

+-----------------------------+--------------------------+------------------------+-----------------------------+------------------------+
|                         Time|root.ln.wf02.wt02.hardware|root.ln.wf02.wt02.status|root.ln.wf01.wt01.temperature|root.ln.wf01.wt01.status|
+-----------------------------+--------------------------+------------------------+-----------------------------+------------------------+
|2017-11-07T23:59:00.000+08:00|                        v1|                   false|                        21.07|                   false|
|2017-11-07T23:58:00.000+08:00|                        v1|                   false|                        22.93|                   false|
|2017-11-07T23:57:00.000+08:00|                        v2|                    true|                        24.39|                    true|
|2017-11-07T23:56:00.000+08:00|                        v2|                    true|                        24.44|                    true|
|2017-11-07T23:55:00.000+08:00|                        v2|                    true|                         25.9|                    true|
|2017-11-07T23:54:00.000+08:00|                        v1|                   false|                        22.52|                   false|
|2017-11-07T23:53:00.000+08:00|                        v2|                    true|                        24.58|                    true|
|2017-11-07T23:52:00.000+08:00|                        v1|                   false|                        20.18|                   false|
|2017-11-07T23:51:00.000+08:00|                        v1|                   false|                        22.24|                   false|
|2017-11-07T23:50:00.000+08:00|                        v2|                    true|                         23.7|                    true|
+-----------------------------+--------------------------+------------------------+-----------------------------+------------------------+
Total line number = 10
It costs 0.016s

更多语法请参照 SQL REFERENCE.

算数运算查询

一元算数运算符

支持的运算符:+, -

输入数据类型要求:INT32, INT64, FLOATDOUBLE

输出数据类型:与输入数据类型一致

二元算数运算符

支持的运算符:+, -, *, /, %

输入数据类型要求:INT32, INT64, FLOATDOUBLE

输出数据类型:DOUBLE

注意:当某个时间戳下左操作数和右操作数都不为空(null)时,二元运算操作才会有输出结果

示例

例如:

select s1, - s1, s2, + s2, s1 + s2, s1 - s2, s1 * s2, s1 / s2, s1 % s2 from root.sg.d1

结果:

+-----------------------------+-------------+--------------+-------------+-------------+-----------------------------+-----------------------------+-----------------------------+-----------------------------+-----------------------------+
|                         Time|root.sg.d1.s1|-root.sg.d1.s1|root.sg.d1.s2|root.sg.d1.s2|root.sg.d1.s1 + root.sg.d1.s2|root.sg.d1.s1 - root.sg.d1.s2|root.sg.d1.s1 * root.sg.d1.s2|root.sg.d1.s1 / root.sg.d1.s2|root.sg.d1.s1 % root.sg.d1.s2|
+-----------------------------+-------------+--------------+-------------+-------------+-----------------------------+-----------------------------+-----------------------------+-----------------------------+-----------------------------+
|1970-01-01T08:00:00.001+08:00|          1.0|          -1.0|          1.0|          1.0|                          2.0|                          0.0|                          1.0|                          1.0|                          0.0|
|1970-01-01T08:00:00.002+08:00|          2.0|          -2.0|          2.0|          2.0|                          4.0|                          0.0|                          4.0|                          1.0|                          0.0|
|1970-01-01T08:00:00.003+08:00|          3.0|          -3.0|          3.0|          3.0|                          6.0|                          0.0|                          9.0|                          1.0|                          0.0|
|1970-01-01T08:00:00.004+08:00|          4.0|          -4.0|          4.0|          4.0|                          8.0|                          0.0|                         16.0|                          1.0|                          0.0|
|1970-01-01T08:00:00.005+08:00|          5.0|          -5.0|          5.0|          5.0|                         10.0|                          0.0|                         25.0|                          1.0|                          0.0|
+-----------------------------+-------------+--------------+-------------+-------------+-----------------------------+-----------------------------+-----------------------------+-----------------------------+-----------------------------+
Total line number = 5
It costs 0.014s

内置时间序列生成函数

时间序列生成函数可接受若干原始时间序列作为输入,产生一列时间序列输出。与聚合函数不同的是,时间序列生成函数的结果集带有时间戳列。

所有的时间序列生成函数都可以接受 * 作为输入,都可以与原始查询混合进行。

数学函数

目前 IoTDB 支持下列数学函数,这些数学函数的行为与这些函数在 Java Math 标准库中对应实现的行为一致。

函数名输入序列类型输出序列类型Java 标准库中的对应实现
SININT32 / INT64 / FLOAT / DOUBLEDOUBLEMath#sin(double)
COSINT32 / INT64 / FLOAT / DOUBLEDOUBLEMath#cos(double)
TANINT32 / INT64 / FLOAT / DOUBLEDOUBLEMath#tan(double)
ASININT32 / INT64 / FLOAT / DOUBLEDOUBLEMath#asin(double)
ACOSINT32 / INT64 / FLOAT / DOUBLEDOUBLEMath#acos(double)
ATANINT32 / INT64 / FLOAT / DOUBLEDOUBLEMath#atan(double)
SINHINT32 / INT64 / FLOAT / DOUBLEDOUBLEMath#sinh(double)
COSHINT32 / INT64 / FLOAT / DOUBLEDOUBLEMath#cosh(double)
TANHINT32 / INT64 / FLOAT / DOUBLEDOUBLEMath#tanh(double)
DEGREESINT32 / INT64 / FLOAT / DOUBLEDOUBLEMath#toDegrees(double)
RADIANSINT32 / INT64 / FLOAT / DOUBLEDOUBLEMath#toRadians(double)
ABSINT32 / INT64 / FLOAT / DOUBLE与输入序列的实际类型一致Math#abs(int) / Math#abs(long) /Math#abs(float) /Math#abs(double)
SIGNINT32 / INT64 / FLOAT / DOUBLEDOUBLEMath#signum(double)
CEILINT32 / INT64 / FLOAT / DOUBLEDOUBLEMath#ceil(double)
FLOORINT32 / INT64 / FLOAT / DOUBLEDOUBLEMath#floor(double)
ROUNDINT32 / INT64 / FLOAT / DOUBLEDOUBLEMath#rint(double)
EXPINT32 / INT64 / FLOAT / DOUBLEDOUBLEMath#exp(double)
LNINT32 / INT64 / FLOAT / DOUBLEDOUBLEMath#log(double)
LOG10INT32 / INT64 / FLOAT / DOUBLEDOUBLEMath#log10(double)
SQRTINT32 / INT64 / FLOAT / DOUBLEDOUBLEMath#sqrt(double)

例如:

select s1, sin(s1), cos(s1), tan(s1) from root.sg1.d1 limit 5 offset 1000;

结果:

+-----------------------------+-------------------+-------------------+--------------------+-------------------+
|                         Time|     root.sg1.d1.s1|sin(root.sg1.d1.s1)| cos(root.sg1.d1.s1)|tan(root.sg1.d1.s1)|
+-----------------------------+-------------------+-------------------+--------------------+-------------------+
|2020-12-10T17:11:49.037+08:00|7360723084922759782| 0.8133527237573284|  0.5817708713544664| 1.3980636773094157|
|2020-12-10T17:11:49.038+08:00|4377791063319964531|-0.8938962705202537|  0.4482738644511651| -1.994085181866842|
|2020-12-10T17:11:49.039+08:00|7972485567734642915| 0.9627757585308978|-0.27030138509681073|-3.5618602479083545|
|2020-12-10T17:11:49.040+08:00|2508858212791964081|-0.6073417341629443| -0.7944406950452296| 0.7644897069734913|
|2020-12-10T17:11:49.041+08:00|2817297431185141819|-0.8419358900502509| -0.5395775727782725| 1.5603611649667768|
+-----------------------------+-------------------+-------------------+--------------------+-------------------+
Total line number = 5
It costs 0.008s

字符串函数

目前 IoTDB 支持下列字符串处理函数:

函数名输入序列类型必要的属性参数输出序列类型功能描述
STRING_CONTAINSTEXTs: 待搜寻的字符串BOOLEAN判断字符串中是否存在s
STRING_MATCHESTEXTregex: Java 标准库风格的正则表达式BOOLEAN判断字符串是否能够被正则表达式regex匹配

例如:

select s1, string_contains(s1, 's'='warn'), string_matches(s1, 'regex'='[^\\s]+37229') from root.sg1.d4;

结果:

+-----------------------------+--------------+-------------------------------------------+------------------------------------------------------+
|                         Time|root.sg1.d4.s1|string_contains(root.sg1.d4.s1, "s"="warn")|string_matches(root.sg1.d4.s1, "regex"="[^\\s]+37229")|
+-----------------------------+--------------+-------------------------------------------+------------------------------------------------------+
|1970-01-01T08:00:00.001+08:00|    warn:-8721|                                       true|                                                 false|
|1970-01-01T08:00:00.002+08:00|  error:-37229|                                      false|                                                  true|
|1970-01-01T08:00:00.003+08:00|     warn:1731|                                       true|                                                 false|
+-----------------------------+--------------+-------------------------------------------+------------------------------------------------------+
Total line number = 3
It costs 0.007s

选择函数

目前 IoTDB 支持如下选择函数:

函数名输入序列类型必要的属性参数输出序列类型功能描述
TOP_KINT32 / INT64 / FLOAT / DOUBLE / TEXTk: 最多选择的数据点数,必须大于 0 小于等于 1000与输入序列的实际类型一致返回某时间序列中值最大的k个数据点。若多于k个数据点的值并列最大,则返回时间戳最小的数据点。
BOTTOM_KINT32 / INT64 / FLOAT / DOUBLE / TEXTk: 最多选择的数据点数,必须大于 0 小于等于 1000与输入序列的实际类型一致返回某时间序列中值最小的k个数据点。若多于k个数据点的值并列最小,则返回时间戳最小的数据点。

例如:

select s1, top_k(s1, 'k'='2'), bottom_k(s1, 'k'='2') from root.sg1.d2 where time > 2020-12-10T20:36:15.530+08:00;

结果:

+-----------------------------+--------------------+------------------------------+---------------------------------+
|                         Time|      root.sg1.d2.s1|top_k(root.sg1.d2.s1, "k"="2")|bottom_k(root.sg1.d2.s1, "k"="2")|
+-----------------------------+--------------------+------------------------------+---------------------------------+
|2020-12-10T20:36:15.531+08:00| 1531604122307244742|           1531604122307244742|                             null|
|2020-12-10T20:36:15.532+08:00|-7426070874923281101|                          null|                             null|
|2020-12-10T20:36:15.533+08:00|-7162825364312197604|          -7162825364312197604|                             null|
|2020-12-10T20:36:15.534+08:00|-8581625725655917595|                          null|             -8581625725655917595|
|2020-12-10T20:36:15.535+08:00|-7667364751255535391|                          null|             -7667364751255535391|
+-----------------------------+--------------------+------------------------------+---------------------------------+
Total line number = 5
It costs 0.006s

趋势计算函数

目前 IoTDB 支持如下趋势计算函数:

函数名输入序列类型输出序列类型功能描述
TIME_DIFFERENCEINT32 / INT64 / FLOAT / DOUBLE / BOOLEAN / TEXTINT64统计序列中某数据点的时间戳与前一数据点时间戳的差。范围内第一个数据点没有对应的结果输出。
DIFFERENCEINT32 / INT64 / FLOAT / DOUBLE与输入序列的实际类型一致统计序列中某数据点的值与前一数据点的值的差。范围内第一个数据点没有对应的结果输出。
NON_NEGATIVE_DIFFERENCEINT32 / INT64 / FLOAT / DOUBLE与输入序列的实际类型一致统计序列中某数据点的值与前一数据点的值的差的绝对值。范围内第一个数据点没有对应的结果输出。
DERIVATIVEINT32 / INT64 / FLOAT / DOUBLEDOUBLE统计序列中某数据点相对于前一数据点的变化率,数量上等同于 DIFFERENCE / TIME_DIFFERENCE。范围内第一个数据点没有对应的结果输出。
NON_NEGATIVE_DERIVATIVEINT32 / INT64 / FLOAT / DOUBLEDOUBLE统计序列中某数据点相对于前一数据点的变化率的绝对值,数量上等同于 NON_NEGATIVE_DIFFERENCE / TIME_DIFFERENCE。范围内第一个数据点没有对应的结果输出。

例如:

select s1, time_difference(s1), difference(s1), non_negative_difference(s1), derivative(s1), non_negative_derivative(s1) from root.sg1.d1 limit 5 offset 1000; 

结果:

+-----------------------------+-------------------+-------------------------------+--------------------------+---------------------------------------+--------------------------+---------------------------------------+
|                         Time|     root.sg1.d1.s1|time_difference(root.sg1.d1.s1)|difference(root.sg1.d1.s1)|non_negative_difference(root.sg1.d1.s1)|derivative(root.sg1.d1.s1)|non_negative_derivative(root.sg1.d1.s1)|
+-----------------------------+-------------------+-------------------------------+--------------------------+---------------------------------------+--------------------------+---------------------------------------+
|2020-12-10T17:11:49.037+08:00|7360723084922759782|                              1|      -8431715764844238876|                    8431715764844238876|    -8.4317157648442388E18|                  8.4317157648442388E18|
|2020-12-10T17:11:49.038+08:00|4377791063319964531|                              1|      -2982932021602795251|                    2982932021602795251|     -2.982932021602795E18|                   2.982932021602795E18|
|2020-12-10T17:11:49.039+08:00|7972485567734642915|                              1|       3594694504414678384|                    3594694504414678384|     3.5946945044146785E18|                  3.5946945044146785E18|
|2020-12-10T17:11:49.040+08:00|2508858212791964081|                              1|      -5463627354942678834|                    5463627354942678834|     -5.463627354942679E18|                   5.463627354942679E18|
|2020-12-10T17:11:49.041+08:00|2817297431185141819|                              1|        308439218393177738|                     308439218393177738|     3.0843921839317773E17|                  3.0843921839317773E17|
+-----------------------------+-------------------+-------------------------------+--------------------------+---------------------------------------+--------------------------+---------------------------------------+
Total line number = 5
It costs 0.014s

常序列生成函数

常序列生成函数用于生成所有数据点的值都相同的时间序列。

常序列生成函数接受一个或者多个时间序列输入,其输出的数据点的时间戳集合是这些输入序列时间戳集合的并集。

目前 IoTDB 支持如下常序列生成函数:

函数名必要的属性参数输出序列类型功能描述
CONSTvalue: 输出的数据点的值
type: 输出的数据点的类型,只能是 INT32 / INT64 / FLOAT / DOUBLE / BOOLEAN / TEXT
由输入属性参数 type 决定根据输入属性 valuetype 输出用户指定的常序列。
PIDOUBLE常序列的值:πdouble 值,圆的周长与其直径的比值,即圆周率,等于 Java标准库 中的Math.PI
EDOUBLE常序列的值:edouble 值,自然对数的底,它等于 Java 标准库 中的 Math.E

例如:

select s1, s2, const(s1, 'value'='1024', 'type'='INT64'), pi(s2), e(s1, s2) from root.sg1.d1; 

结果:

select s1, s2, const(s1, 'value'='1024', 'type'='INT64'), pi(s2), e(s1, s2) from root.sg1.d1; 
+-----------------------------+--------------+--------------+-----------------------------------------------------+------------------+---------------------------------+
|                         Time|root.sg1.d1.s1|root.sg1.d1.s2|const(root.sg1.d1.s1, "value"="1024", "type"="INT64")|pi(root.sg1.d1.s2)|e(root.sg1.d1.s1, root.sg1.d1.s2)|
+-----------------------------+--------------+--------------+-----------------------------------------------------+------------------+---------------------------------+
|1970-01-01T08:00:00.000+08:00|           0.0|           0.0|                                                 1024| 3.141592653589793|                2.718281828459045|
|1970-01-01T08:00:00.001+08:00|           1.0|          null|                                                 1024|              null|                2.718281828459045|
|1970-01-01T08:00:00.002+08:00|           2.0|          null|                                                 1024|              null|                2.718281828459045|
|1970-01-01T08:00:00.003+08:00|          null|           3.0|                                                 null| 3.141592653589793|                2.718281828459045|
|1970-01-01T08:00:00.004+08:00|          null|           4.0|                                                 null| 3.141592653589793|                2.718281828459045|
+-----------------------------+--------------+--------------+-----------------------------------------------------+------------------+---------------------------------+
Total line number = 5
It costs 0.005s

数据类型转换函数

当前IoTDB支持6种数据类型,其中包括INT32、INT64、FLOAT、DOUBLE、BOOLEAN以及TEXT。当我们对数据进行查询或者计算时可能需要进行数据类型的转换, 比如说将TEXT转换为INT32,或者提高数据精度,比如说将FLOAT转换为DOUBLE。所以,IoTDB支持使用cast函数对数据类型进行转换。

函数名必要的属性参数输出序列类型功能类型
CASTtype:输出的数据点的类型,只能是 INT32 / INT64 / FLOAT / DOUBLE / BOOLEAN / TEXT由输入属性参数type决定将数据转换为type参数指定的类型。
类型转换说明

1.当INT32、INT64类型的值不为0时,FLOAT与DOUBLE类型的值不为0.0时,TEXT类型不为空字符串或者“false”时,转换为BOOLEAN类型时值为true,否则为false。

2.当BOOLEAN类型的值为true时,转换为INT32与INT64类型的值为1,转换为FLOAT或者DOUBLE类型时值为1.0,转换为TEXT类型时值为“true”。当BOOLEAN类型的值为false时,转换为INT32与INT64类型的值为0,转换为FLOAT或者DOUBLE类型时值为0.0,转换为TEXT类型时值为“false”。

3.当TEXT类型转换为INT32、INT64、FLOAT类型时,会先将TEXT类型的数据转换为DOUBLE类型,然后再转换为对应的类型,此时可能会存在损失精度的问题。如果无法转换的话则直接跳过。

演示

测试数据:

IoTDB> select text from root.test;
+-----------------------------+--------------+
|                         Time|root.test.text|
+-----------------------------+--------------+
|1970-01-01T08:00:00.001+08:00|           1.1|
|1970-01-01T08:00:00.002+08:00|             1|
|1970-01-01T08:00:00.003+08:00|   hello world|
|1970-01-01T08:00:00.004+08:00|         false|
+-----------------------------+--------------+

SQL语句:

select cast(text, 'type'='BOOLEAN'), cast(text, 'type'='INT32'), cast(text, 'type'='INT64'), cast(text, 'type'='FLOAT'), cast(text, 'type'='DOUBLE') from root.test;

结果:

+-----------------------------+--------------------------------------+------------------------------------+------------------------------------+------------------------------------+-------------------------------------+
|                         Time|cast(root.test.text, "type"="BOOLEAN")|cast(root.test.text, "type"="INT32")|cast(root.test.text, "type"="INT64")|cast(root.test.text, "type"="FLOAT")|cast(root.test.text, "type"="DOUBLE")|
+-----------------------------+--------------------------------------+------------------------------------+------------------------------------+------------------------------------+-------------------------------------+
|1970-01-01T08:00:00.001+08:00|                                  true|                                   1|                                   1|                                 1.1|                                  1.1|
|1970-01-01T08:00:00.002+08:00|                                  true|                                   1|                                   1|                                 1.0|                                  1.0|
|1970-01-01T08:00:00.003+08:00|                                  true|                                null|                                null|                                null|                                 null|
|1970-01-01T08:00:00.004+08:00|                                 false|                                null|                                null|                                null|                                 null|
+-----------------------------+--------------------------------------+------------------------------------+------------------------------------+------------------------------------+-------------------------------------+
Total line number = 4
It costs 0.078s

自定义时间序列生成函数

请参考 UDF (用户定义函数)

已知的自定义时间序列生成函数库实现:

  • IoTDB-Quality,一个关于数据质量的 UDF 库实现,包括数据画像、数据质量评估与修复等一系列函数。

嵌套表达式

IoTDB 支持在 select 字句中执行由**数字常量,时间序列、算数运算表达式和时间序列生成函数(包括用户自定义函数)**组成的任意嵌套表达式。

语法

下面是 select 子句的语法定义:

selectClause
    : SELECT resultColumn (',' resultColumn)*
    ;

resultColumn
    : expression (AS ID)?
    ;

expression
    : '(' expression ')'
    | '-' expression
    | expression ('*' | '/' | '%') expression
    | expression ('+' | '-') expression
    | functionName '(' expression (',' expression)* functionAttribute* ')'
    | timeSeriesSuffixPath
    | number
    ;

示例

输入:

select a,
       b,
       ((a + 1) * 2 - 1) % 2 + 1.5,
       sin(a + sin(a + sin(b))),
       -(a + b) * (sin(a + b) * sin(a + b) + cos(a + b) * cos(a + b))
from root.sg1.d1;

结果:

+-----------------------------+-------------+-------------+-------------------------------------------+------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|                         Time|root.sg1.d1.a|root.sg1.d1.b|((((root.sg1.d1.a + 1) * 2) - 1) % 2) + 1.5|sin(root.sg1.d1.a + sin(root.sg1.d1.a + sin(root.sg1.d1.b)))|-root.sg1.d1.a + root.sg1.d1.b * ((sin(root.sg1.d1.a + root.sg1.d1.b) * sin(root.sg1.d1.a + root.sg1.d1.b)) + (cos(root.sg1.d1.a + root.sg1.d1.b) * cos(root.sg1.d1.a + root.sg1.d1.b)))|
+-----------------------------+-------------+-------------+-------------------------------------------+------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|1970-01-01T08:00:00.000+08:00|            0|            0|                                        2.5|                                                         0.0|                                                                                                                                                                                    -0.0|
|1970-01-01T08:00:00.001+08:00|            1|            1|                                        2.5|                                          0.9238430524420609|                                                                                                                                                                                    -2.0|
|1970-01-01T08:00:00.002+08:00|            2|            2|                                        2.5|                                          0.7903505371876317|                                                                                                                                                                                    -4.0|
+-----------------------------+-------------+-------------+-------------------------------------------+------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
Total line number = 3
It costs 0.170s

空值填充

在 IoTDB 的实际使用中,当进行时间序列的查询操作时,可能会出现在某些时间点值为 null 的情况,这会妨碍用户进行进一步的分析。 为了更好地反映数据更改的程度,用户希望可以自动填充缺失值。 因此,IoTDB 系统引入了自动填充功能。

自动填充功能是指对单列或多列执行时间序列查询时,根据用户指定的方法和有效时间范围填充空值。 如果查询点的值不为 null,则填充功能将不起作用。

填充方法

IoTDB 目前支持 previous, linear, value 三种空值填充方式,数据类型和支持的填充方法如表 3-1 所示。

表 3-1 数据类型和支持的填充方法

数据类型支持的填充方法
booleanprevious, value
int32previous, linear, value
int64previous, linear, value
floatprevious, linear, value
doubleprevious, linear, value
textprevious, value

注意:在 Fill 语句中只能指定一种填充方法。空值填充兼容 0.12 版本及以前的语法(即 fill((<data_type>[<fill_method>(, <before_range>, <after_range>)?])+)),但老的语法也不能同时指定多种填充方式

单点补空值查询

当某一特定时间戳的数据为空时,可以使用单值填充对空值进行填充,详细说明如下:

  • Previous 方式

当查询的时间戳值为空时,将使用前一个时间戳的值来填充空白。 形式化的先前方法如下:

select <path> from <prefixPath> where time = <T> fill(previous(, <before_range>)?)

表 3-2 给出了所有参数的详细说明。

表 3-2 previous 填充参数列表

参数名称(不区分大小写)解释
path, prefixPath查询路径; 必填项
T查询时间戳(只能指定一个); 必填项
before_range表示前一种方法的有效时间范围。 当 [T-before_range,T] 范围内的值存在时,前一种方法将起作用。 如果未指定 before_range,则 before_range 会使用默认值 default_fill_interval; -1 表示无穷大; 可选字段

在这里,我们举一个使用先前方法填充空值的示例。 SQL 语句如下:

select temperature from root.sgcc.wf03.wt01 where time = 2017-11-01T16:37:50.000 fill(previous, 1m) 

意思是:

由于时间根目录 root.sgcc.wf03.wt01.temperature 在 2017-11-01T16:37:50.000 为空,因此系统使用以前的时间戳 2017-11-01T16:37:00.000(且时间戳位于 [2017-11-01T16:36:50.000, 2017-11-01T16:37:50.000] 范围)进行填充和显示。

样例数据中, 该语句的执行结果如下所示:

+-----------------------------+-------------------------------+
|                         Time|root.sgcc.wf03.wt01.temperature|
+-----------------------------+-------------------------------+
|2017-11-01T16:37:50.000+08:00|                          21.93|
+-----------------------------+-------------------------------+
Total line number = 1
It costs 0.016s

值得注意的是,如果在指定的有效时间范围内没有值,系统将不会填充空值,如下所示:

IoTDB> select temperature from root.sgcc.wf03.wt01 where time = 2017-11-01T16:37:50.000 fill(previous, 1s) 
+-----------------------------+-------------------------------+
|                         Time|root.sgcc.wf03.wt01.temperature|
+-----------------------------+-------------------------------+
|2017-11-01T16:37:50.000+08:00|                           null|
+-----------------------------+-------------------------------+
Total line number = 1
It costs 0.004s
  • Linear 方法

当查询的时间戳值为空时,将使用前一个和下一个时间戳的值来填充空白。 形式化线性方法如下:

select <path> from <prefixPath> where time = <T> fill(linear(, <before_range>, <after_range>)?)

表 3-3 中给出了所有参数的详细说明。

表 3-3 线性填充参数列表

参数名称(不区分大小写)解释
path, prefixPath查询路径; 必填项
T查询时间戳(只能指定一个); 必填项
before_range, after_range表示线性方法的有效时间范围。 当 [T - before_range,T + after_range] 范围内的值存在时,前一种方法将起作用。 如果未明确指定 before_range 和 after_range,则使用 default_fill_interval。 -1 表示无穷大; 可选字段

需要注意的是一旦时间序列在查询时间戳 T 时刻存在有效值,线性填充就会使用这个值作为结果返回。 除此之外,如果在 [T - before_range, T] 或 [T, T + after_range] 两个范围中任意一个范围内不存在有效填充值,则线性填充返回 null 值。

在这里,我们举一个使用线性方法填充空值的示例。 SQL 语句如下:

select temperature from root.sgcc.wf03.wt01 where time = 2017-11-01T16:37:50.000 fill(linear, 1m, 1m)

意思是:

由于时间根目录 root.sgcc.wf03.wt01.temperature 在 2017-11-01T16:37:50.000 为空,因此系统使用以前的时间戳 2017-11-01T16:37:00.000(且时间戳位于 [2017-11-01T16:36:50.000,2017-11-01T16:37:50.000) 时间范围)及其值 21.927326,下一个时间戳记 2017-11-01T16:38:00.000(且时间戳记位于 (2017-11-01T16:37:50.000, 2017-11-01T16:38:50.000) 时间范围)及其值 25.311783 以执行线性拟合计算:

21.927326 +(25.311783-21.927326)/ 60s * 50s = 24.747707

样例数据, 该语句的执行结果如下所示:

+-----------------------------+-------------------------------+
|                         Time|root.sgcc.wf03.wt01.temperature|
+-----------------------------+-------------------------------+
|2017-11-01T16:37:50.000+08:00|                      24.747707|
+-----------------------------+-------------------------------+
Total line number = 1
It costs 0.017s
  • Value方法

当查询的时间戳值为空时,将使用给定的值来填充空白。 特定值填充方法如下:

select <path> from <prefixPath> where time = <T> fill(constant)

表 3-4 中给出了所有参数的详细说明。

表 3-4 特定值填充参数列表

参数名称(不区分大小写)解释
path, prefixPath查询路径; 必填项
T查询时间戳(只能指定一个); 必填项
constant给定的填充值;必填项

需要注意的是一旦时间序列在查询时间戳T时刻存在有效值,特定值填充就会使用这个值作为结果返回。

在这里,我们举一个使用特定值方法填充空值的示例。 SQL语句如下:

select temperature from root.sgcc.wf03.wt01 where time = 2017-11-01T16:37:50.000 fill(2.0)

意思是:

由于时间根目录root.sgcc.wf03.wt01.temperature在2017-11-01T16:37:50.000为空,因此使用给定的值2.0进行填充:

样例数据, 该语句的执行结果如下所示:

+-----------------------------+-------------------------------+
|                         Time|root.sgcc.wf03.wt01.temperature|
+-----------------------------+-------------------------------+
|2017-11-01T16:37:50.000+08:00|                           2.0 |
+-----------------------------+-------------------------------+
Total line number = 1
It costs 0.007s

在使用 VALUE 方法填充时需要注意,如果查询结果的数据类型与输入常量值不同,IoTDB 将不进行填充

示例:

select temperature from root.sgcc.wf03.wt01 where time = 2017-11-01T16:37:50.000 fill('test')

结果:

+-----------------------------+-------------------------------+
|                         Time|root.sgcc.wf03.wt01.temperature|
+-----------------------------+-------------------------------+
|2017-11-01T16:37:50.000+08:00|                          null |
+-----------------------------+-------------------------------+
Total line number = 1
It costs 0.007s

聚合查询

本章节主要介绍聚合查询的相关示例, 主要使用的是 IoTDB SELECT 语句的聚合查询函数。

统计总点数

select count(status) from root.ln.wf01.wt01;

结果:

+-------------------------------+
|count(root.ln.wf01.wt01.status)|
+-------------------------------+
|                          10080|
+-------------------------------+
Total line number = 1
It costs 0.016s

路径层级分组聚合

在时间序列层级结构中,分层聚合查询用于对某一层级下同名的序列进行聚合查询。 这里使用 LEVEL 来统计指定需要聚合的层级,该语句约定 root 为第 0 层序列,若统计 “root.ln” 下所有序列则需指定 level 为 1。

例如:不同存储组下均存在名为 status 的序列, 如 “root.ln.wf01.wt01.status”, “root.ln.wf02.wt02.status”, 以及 " root.sgcc.wf03.wt01.status", 如果需要统计不同存储组下 status 序列的数据点个数,使用以下查询:

select count(status)
from root.**
group by level = 1

运行结果为:

+-------------------------+---------------------------+
|count(root.ln.*.*.status)|count(root.sgcc.*.*.status)|
+-------------------------+---------------------------+
|                    20160|                      10080|
+-------------------------+---------------------------+
Total line number = 1
It costs 0.003s

同理,如果需要统计不同设备下 status 序列的数据点个数,可以规定 level = 3,

select count(status)
from root.**
group by level = 3

运行结果为:

+---------------------------+---------------------------+
|count(root.*.*.wt01.status)|count(root.*.*.wt02.status)|
+---------------------------+---------------------------+
|                      20160|                      10080|
+---------------------------+---------------------------+
Total line number = 1
It costs 0.003s

注意,这时会将存储组 lnsgcc 下名为 wt01 的设备视为同名设备聚合在一起。而如果需要进一步统计不同存储组下的不同设备中 status 序列的数据点个数,可以使用以下查询:

select count(status)
from root.**
group by level = 1, 3

运行结果为:

+----------------------------+----------------------------+------------------------------+
|count(root.ln.*.wt01.status)|count(root.ln.*.wt02.status)|count(root.sgcc.*.wt01.status)|
+----------------------------+----------------------------+------------------------------+
|                       10080|                       10080|                         10080|
+----------------------------+----------------------------+------------------------------+
Total line number = 1
It costs 0.003s

假设需要查询所有序列下温度传感器 temperature 的最大值,可以使用下列查询语句:

select max_value(temperature)
from root.**
group by level = 0

运行结果:

+---------------------------------+
|max_value(root.*.*.*.temperature)|
+---------------------------------+
|                             26.0|
+---------------------------------+
Total line number = 1
It costs 0.013s

上面的查询都是针对某一个传感器,特别地,如果想要查询某一层级下所有传感器拥有的总数据点数,则需要显式规定测点为 *

select count(*)
from root.ln.**
group by level = 2

运行结果:

+----------------------+----------------------+
|count(root.*.wf01.*.*)|count(root.*.wf02.*.*)|
+----------------------+----------------------+
|                 20160|                 20160|
+----------------------+----------------------+
Total line number = 1
It costs 0.013s

分层聚合查询也可被用于其他聚合函数,当前所支持的聚合函数为:count, sum, avg, last_value, first_value, min_time, max_time, min_value, max_value, extreme

对于 sum, avg, min_value, max_value, extreme 五种聚合函数,需保证所有聚合的时间序列数据类型相同。其他聚合函数没有此限制。

时间区间分组聚合

本章节主要介绍时间区间分组聚合查询的相关示例, 主要使用的是 IoTDB SELECT 语句的 GROUP BY 子句, 该子句是 IoTDB 中用于根据用户给定划分条件对结果集进行划分,并对已划分的结果集进行聚合计算的语句。 IoTDB 支持根据时间间隔和自定义的滑动步长(默认值与时间间隔相同,自定义的值必须大于等于时间间隔)对结果集进行划分,默认结果按照时间升序排列。 同时,您也可以使用 Java JDBC 标准接口来执行相关的查询语句。

GROUP BY 语句为用户提供三类指定参数:

  • 参数 1:时间轴显示时间窗参数
  • 参数 2:划分时间轴的时间间隔参数(必须为正数)
  • 参数 3:滑动步长(可选参数,默认值与时间间隔相同,自定义的值必须大于等于时间间隔)

三类参数的实际含义已经在下图中指出,这三类参数里,第三个参数是可选的。 接下来,我们将给出三种典型的降频聚合查询的例子: 滑动步长未指定, 指定滑动步长, 带值过滤条件。

未指定滑动步长的时间区间分组聚合查询

对应的 SQL 语句是:

select count(status), max_value(temperature) from root.ln.wf01.wt01 group by ([2017-11-01T00:00:00, 2017-11-07T23:00:00),1d);

这条查询的含义是:

由于用户没有指定滑动步长,滑动步长将会被默认设置为跟时间间隔参数相同,也就是1d

上面这个例子的第一个参数是显示窗口参数,决定了最终的显示范围是 [2017-11-01T00:00:00, 2017-11-07T23:00:00)。

上面这个例子的第二个参数是划分时间轴的时间间隔参数,将1d当作划分间隔,显示窗口参数的起始时间当作分割原点,时间轴即被划分为连续的时间间隔:[0,1d), [1d, 2d), [2d, 3d) 等等。

然后系统将会用 WHERE 子句中的时间和值过滤条件以及 GROUP BY 语句中的第一个参数作为数据的联合过滤条件,获得满足所有过滤条件的数据(在这个例子里是在 [2017-11-01T00:00:00, 2017-11-07 T23:00:00) 这个时间范围的数据),并把这些数据映射到之前分割好的时间轴中(这个例子里是从 2017-11-01T00:00:00 到 2017-11-07T23:00:00:00 的每一天)

每个时间间隔窗口内都有数据,SQL 执行后的结果集如下所示:

+-----------------------------+-------------------------------+----------------------------------------+
|                         Time|count(root.ln.wf01.wt01.status)|max_value(root.ln.wf01.wt01.temperature)|
+-----------------------------+-------------------------------+----------------------------------------+
|2017-11-01T00:00:00.000+08:00|                           1440|                                    26.0|
|2017-11-02T00:00:00.000+08:00|                           1440|                                    26.0|
|2017-11-03T00:00:00.000+08:00|                           1440|                                   25.99|
|2017-11-04T00:00:00.000+08:00|                           1440|                                    26.0|
|2017-11-05T00:00:00.000+08:00|                           1440|                                    26.0|
|2017-11-06T00:00:00.000+08:00|                           1440|                                   25.99|
|2017-11-07T00:00:00.000+08:00|                           1380|                                    26.0|
+-----------------------------+-------------------------------+----------------------------------------+
Total line number = 7
It costs 0.024s
指定滑动步长的时间区间分组聚合查询

对应的 SQL 语句是:

select count(status), max_value(temperature) from root.ln.wf01.wt01 group by ([2017-11-01 00:00:00, 2017-11-07 23:00:00), 3h, 1d);

这条查询的含义是:

由于用户指定了滑动步长为1d,GROUP BY 语句执行时将会每次把时间间隔往后移动一天的步长,而不是默认的 3 小时。

也就意味着,我们想要取从 2017-11-01 到 2017-11-07 每一天的凌晨 0 点到凌晨 3 点的数据。

上面这个例子的第一个参数是显示窗口参数,决定了最终的显示范围是 [2017-11-01T00:00:00, 2017-11-07T23:00:00)。

上面这个例子的第二个参数是划分时间轴的时间间隔参数,将3h当作划分间隔,显示窗口参数的起始时间当作分割原点,时间轴即被划分为连续的时间间隔:[2017-11-01T00:00:00, 2017-11-01T03:00:00), [2017-11-02T00:00:00, 2017-11-02T03:00:00), [2017-11-03T00:00:00, 2017-11-03T03:00:00) 等等。

上面这个例子的第三个参数是每次时间间隔的滑动步长。

然后系统将会用 WHERE 子句中的时间和值过滤条件以及 GROUP BY 语句中的第一个参数作为数据的联合过滤条件,获得满足所有过滤条件的数据(在这个例子里是在 [2017-11-01T00:00:00, 2017-11-07 T23:00:00) 这个时间范围的数据),并把这些数据映射到之前分割好的时间轴中(这个例子里是从 2017-11-01T00:00:00 到 2017-11-07T23:00:00:00 的每一天的凌晨 0 点到凌晨 3 点)

每个时间间隔窗口内都有数据,SQL 执行后的结果集如下所示:

+-----------------------------+-------------------------------+----------------------------------------+
|                         Time|count(root.ln.wf01.wt01.status)|max_value(root.ln.wf01.wt01.temperature)|
+-----------------------------+-------------------------------+----------------------------------------+
|2017-11-01T00:00:00.000+08:00|                            180|                                   25.98|
|2017-11-02T00:00:00.000+08:00|                            180|                                   25.98|
|2017-11-03T00:00:00.000+08:00|                            180|                                   25.96|
|2017-11-04T00:00:00.000+08:00|                            180|                                   25.96|
|2017-11-05T00:00:00.000+08:00|                            180|                                    26.0|
|2017-11-06T00:00:00.000+08:00|                            180|                                   25.85|
|2017-11-07T00:00:00.000+08:00|                            180|                                   25.99|
+-----------------------------+-------------------------------+----------------------------------------+
Total line number = 7
It costs 0.006s
按照自然月份的时间区间分组聚合查询

对应的 SQL 语句是:

select count(status) from root.ln.wf01.wt01 where time > 2017-11-01T01:00:00 group by([2017-11-01T00:00:00, 2019-11-07T23:00:00), 1mo, 2mo);

这条查询的含义是:

由于用户指定了滑动步长为2mo,GROUP BY 语句执行时将会每次把时间间隔往后移动 2 个自然月的步长,而不是默认的 1 个自然月。

也就意味着,我们想要取从 2017-11-01 到 2019-11-07 每 2 个自然月的第一个月的数据。

上面这个例子的第一个参数是显示窗口参数,决定了最终的显示范围是 [2017-11-01T00:00:00, 2019-11-07T23:00:00)。

起始时间为 2017-11-01T00:00:00,滑动步长将会以起始时间作为标准按月递增,取当月的 1 号作为时间间隔的起始时间。

上面这个例子的第二个参数是划分时间轴的时间间隔参数,将1mo当作划分间隔,显示窗口参数的起始时间当作分割原点,时间轴即被划分为连续的时间间隔:[2017-11-01T00:00:00, 2017-12-01T00:00:00), [2018-02-01T00:00:00, 2018-03-01T00:00:00), [2018-05-03T00:00:00, 2018-06-01T00:00:00) 等等。

上面这个例子的第三个参数是每次时间间隔的滑动步长。

然后系统将会用 WHERE 子句中的时间和值过滤条件以及 GROUP BY 语句中的第一个参数作为数据的联合过滤条件,获得满足所有过滤条件的数据(在这个例子里是在 [2017-11-01T00:00:00, 2019-11-07T23:00:00) 这个时间范围的数据),并把这些数据映射到之前分割好的时间轴中(这个例子里是从 2017-11-01T00:00:00 到 2019-11-07T23:00:00:00 的每两个自然月的第一个月)

每个时间间隔窗口内都有数据,SQL 执行后的结果集如下所示:

+-----------------------------+-------------------------------+
|                         Time|count(root.ln.wf01.wt01.status)|
+-----------------------------+-------------------------------+
|2017-11-01T00:00:00.000+08:00|                            259|
|2018-01-01T00:00:00.000+08:00|                            250|
|2018-03-01T00:00:00.000+08:00|                            259|
|2018-05-01T00:00:00.000+08:00|                            251|
|2018-07-01T00:00:00.000+08:00|                            242|
|2018-09-01T00:00:00.000+08:00|                            225|
|2018-11-01T00:00:00.000+08:00|                            216|
|2019-01-01T00:00:00.000+08:00|                            207|
|2019-03-01T00:00:00.000+08:00|                            216|
|2019-05-01T00:00:00.000+08:00|                            207|
|2019-07-01T00:00:00.000+08:00|                            199|
|2019-09-01T00:00:00.000+08:00|                            181|
|2019-11-01T00:00:00.000+08:00|                             60|
+-----------------------------+-------------------------------+

对应的 SQL 语句是:

select count(status) from root.ln.wf01.wt01 group by([2017-10-31T00:00:00, 2019-11-07T23:00:00), 1mo, 2mo);

这条查询的含义是:

由于用户指定了滑动步长为2mo,GROUP BY 语句执行时将会每次把时间间隔往后移动 2 个自然月的步长,而不是默认的 1 个自然月。

也就意味着,我们想要取从 2017-10-31 到 2019-11-07 每 2 个自然月的第一个月的数据。

与上述示例不同的是起始时间为 2017-10-31T00:00:00,滑动步长将会以起始时间作为标准按月递增,取当月的 31 号(即最后一天)作为时间间隔的起始时间。若起始时间设置为 30 号,滑动步长会将时间间隔的起始时间设置为当月 30 号,若不存在则为最后一天。

上面这个例子的第一个参数是显示窗口参数,决定了最终的显示范围是 [2017-10-31T00:00:00, 2019-11-07T23:00:00)。

上面这个例子的第二个参数是划分时间轴的时间间隔参数,将1mo当作划分间隔,显示窗口参数的起始时间当作分割原点,时间轴即被划分为连续的时间间隔:[2017-10-31T00:00:00, 2017-11-31T00:00:00), [2018-02-31T00:00:00, 2018-03-31T00:00:00), [2018-05-31T00:00:00, 2018-06-31T00:00:00) 等等。

上面这个例子的第三个参数是每次时间间隔的滑动步长。

然后系统将会用 WHERE 子句中的时间和值过滤条件以及 GROUP BY 语句中的第一个参数作为数据的联合过滤条件,获得满足所有过滤条件的数据(在这个例子里是在 [2017-10-31T00:00:00, 2019-11-07T23:00:00) 这个时间范围的数据),并把这些数据映射到之前分割好的时间轴中(这个例子里是从 2017-10-31T00:00:00 到 2019-11-07T23:00:00:00 的每两个自然月的第一个月)

每个时间间隔窗口内都有数据,SQL 执行后的结果集如下所示:

+-----------------------------+-------------------------------+
|                         Time|count(root.ln.wf01.wt01.status)|
+-----------------------------+-------------------------------+
|2017-10-31T00:00:00.000+08:00|                            251|
|2017-12-31T00:00:00.000+08:00|                            250|
|2018-02-28T00:00:00.000+08:00|                            259|
|2018-04-30T00:00:00.000+08:00|                            250|
|2018-06-30T00:00:00.000+08:00|                            242|
|2018-08-31T00:00:00.000+08:00|                            225|
|2018-10-31T00:00:00.000+08:00|                            216|
|2018-12-31T00:00:00.000+08:00|                            208|
|2019-02-28T00:00:00.000+08:00|                            216|
|2019-04-30T00:00:00.000+08:00|                            208|
|2019-06-30T00:00:00.000+08:00|                            199|
|2019-08-31T00:00:00.000+08:00|                            181|
|2019-10-31T00:00:00.000+08:00|                             69|
+-----------------------------+-------------------------------+
左开右闭区间

每个区间的结果时间戳为区间右端点,对应的 SQL 语句是:

select count(status) from root.ln.wf01.wt01 group by ((2017-11-01T00:00:00, 2017-11-07T23:00:00],1d);

这条查询语句的时间区间是左开右闭的,结果中不会包含时间点 2017-11-01 的数据,但是会包含时间点 2017-11-07 的数据。

SQL 执行后的结果集如下所示:

+-----------------------------+-------------------------------+
|                         Time|count(root.ln.wf01.wt01.status)|
+-----------------------------+-------------------------------+
|2017-11-02T00:00:00.000+08:00|                           1440|
|2017-11-03T00:00:00.000+08:00|                           1440|
|2017-11-04T00:00:00.000+08:00|                           1440|
|2017-11-05T00:00:00.000+08:00|                           1440|
|2017-11-06T00:00:00.000+08:00|                           1440|
|2017-11-07T00:00:00.000+08:00|                           1440|
|2017-11-07T23:00:00.000+08:00|                           1380|
+-----------------------------+-------------------------------+
Total line number = 7
It costs 0.004s

降采样补空值查询

IoTDB 支持对原降采样结果进行空值填充,previous, linear, value 填充方式均可作用于查询语句中的任一聚合算子,但一条查询语句只能使用一种空值填充方式。此外,使用时需要注意以下两点:

  • GroupByFill 在任何情形下都不会填充 count 的聚合结果,因为在 IoTDB 中,若某个查询区间不存在任何数据,count 的聚合结果为 0
  • GroupByFill 会分类处理 sum 的聚合结果,在 IoTDB 中,若某个查询区间不存在任何数据,sum 的聚合结果为 null,将被 GroupByFill 填充;若某个查询区间内 sum 的聚合结果恰好为 0,那么 GroupByFill 不会填充这个值

降采样补空值查询语法同单点补空值查询语法相似,下面列出简单的示例和使用细节:

PREVIOUS 和 PREVIOUSUNTILLAST 的区别
  • PREVIOUS:只要空值前边有值,就会用其填充空值
  • PREVIOUSUNTILLAST:不会填充此序列最新点后的空值

首先检查一下 root.ln.wf01.wt01.temperature 在时间 2017-11-07T23:49:00 以后的值

IoTDB> SELECT temperature FROM root.ln.wf01.wt01 where time >= 2017-11-07T23:49:00
+-----------------------------+-----------------------------+
|                         Time|root.ln.wf01.wt01.temperature|
+-----------------------------+-----------------------------+
|2017-11-07T23:49:00.000+08:00|                         23.7|
|2017-11-07T23:51:00.000+08:00|                        22.24|
|2017-11-07T23:53:00.000+08:00|                        24.58|
|2017-11-07T23:54:00.000+08:00|                        22.52|
|2017-11-07T23:57:00.000+08:00|                        24.39|
|2017-11-08T00:00:00.000+08:00|                        21.07|
+-----------------------------+-----------------------------+
Total line number = 6
It costs 0.010s

root.ln.wf01.wt01.temperature 最早时间和值是 2017-11-07T23:49:00 和 23.7;最后时间和值是 2017-11-08T00:00:00 和 21.07

SQL 示例:

SELECT last_value(temperature) FROM root.ln.wf01.wt01 GROUP BY([2017-11-07T23:50:00, 2017-11-07T23:59:00),1m) FILL (PREVIOUSUNTILLAST);
SELECT last_value(temperature) FROM root.ln.wf01.wt01 GROUP BY([2017-11-07T23:50:00, 2017-11-07T23:59:00),1m) FILL (PREVIOUS);

结果:

IoTDB> SELECT last_value(temperature) FROM root.ln.wf01.wt01 GROUP BY([2017-11-07T23:50:00, 2017-11-07T23:59:00),1m) FILL (PREVIOUSUNTILLAST);
+-----------------------------+-----------------------------------------+
|                         Time|last_value(root.ln.wf01.wt01.temperature)|
+-----------------------------+-----------------------------------------+
|2017-11-07T23:50:00.000+08:00|                                     null|
|2017-11-07T23:51:00.000+08:00|                                    22.24|
|2017-11-07T23:52:00.000+08:00|                                    22.24|
|2017-11-07T23:53:00.000+08:00|                                    24.58|
|2017-11-07T23:54:00.000+08:00|                                    22.52|
|2017-11-07T23:55:00.000+08:00|                                    22.52|
|2017-11-07T23:56:00.000+08:00|                                    22.52|
|2017-11-07T23:57:00.000+08:00|                                    24.39|
|2017-11-07T23:58:00.000+08:00|                                     null|
+-----------------------------+-----------------------------------------+
Total line number = 9
It costs 0.007s

IoTDB> SELECT last_value(temperature) FROM root.ln.wf01.wt01 GROUP BY([2017-11-07T23:50:00, 2017-11-07T23:59:00),1m) FILL (PREVIOUS);
+-----------------------------+-----------------------------------------+
|                         Time|last_value(root.ln.wf01.wt01.temperature)|
+-----------------------------+-----------------------------------------+
|2017-11-07T23:50:00.000+08:00|                                     null|
|2017-11-07T23:51:00.000+08:00|                                    22.24|
|2017-11-07T23:52:00.000+08:00|                                    22.24|
|2017-11-07T23:53:00.000+08:00|                                    24.58|
|2017-11-07T23:54:00.000+08:00|                                    22.52|
|2017-11-07T23:55:00.000+08:00|                                    22.52|
|2017-11-07T23:56:00.000+08:00|                                    22.52|
|2017-11-07T23:57:00.000+08:00|                                    24.39|
|2017-11-07T23:58:00.000+08:00|                                    24.39|
+-----------------------------+-----------------------------------------+
Total line number = 9
It costs 0.006s

解释:

使用 PREVIOUSUNTILLAST 将不会填充 2017-11-07T23:57:00 以后的值。

第一个值与最后一个值的填充

IoTDB 的空值填充方式可以分为 PreviousFill, LinearFill, ValueFill 三大类。其中,PreviousFill 需要知道空值前的第一个非空数据,LinearFill 需要知道空值前后的第一个非空数据才能进行填充。假使某条查询语句返回的结果中第一个或最后一个值为空,就可能导致结果集在首尾存在一段连续的空值,不满足 GroupByFill 的业务期望。

在上例中,第一个时间区间 [2017-11-07T23:50:00, 2017-11-07T23:51:00) 内没有任何数据,上一个有数据的时间区间是 [2017-11-01T23:49:00, 2017-11-07T23:50:00),可以通过设置 PREVIOUS 填充向前查询参数 beforeRange 来填充第一个区间的数据,示例如下:

SELECT last_value(temperature) FROM root.ln.wf01.wt01 GROUP BY([2017-11-07T23:50:00, 2017-11-07T23:59:00),1m) FILL (PREVIOUS, 1m);

结果:

IoTDB> SELECT last_value(temperature) FROM root.ln.wf01.wt01 GROUP BY([2017-11-07T23:50:00, 2017-11-07T23:59:00),1m) FILL (PREVIOUS, 1m);
+-----------------------------+-----------------------------------------+
|                         Time|last_value(root.ln.wf01.wt01.temperature)|
+-----------------------------+-----------------------------------------+
|2017-11-07T23:50:00.000+08:00|                                     23.7|
|2017-11-07T23:51:00.000+08:00|                                    22.24|
|2017-11-07T23:52:00.000+08:00|                                    22.24|
|2017-11-07T23:53:00.000+08:00|                                    24.58|
|2017-11-07T23:54:00.000+08:00|                                    22.52|
|2017-11-07T23:55:00.000+08:00|                                    22.52|
|2017-11-07T23:56:00.000+08:00|                                     null|
|2017-11-07T23:57:00.000+08:00|                                    24.39|
|2017-11-07T23:58:00.000+08:00|                                    24.39|
+-----------------------------+-----------------------------------------+
Total line number = 9
It costs 0.005s

解释:

为了不与原有语义冲突,当不设置 before_range, after_range 参数时,GroupByFill 的空值填充取空值的前一个/后一个非空值完成;当设置 before_range, after_range 参数时,设空值所在记录的时间戳为 t,GroupByFill 取 [t-before_range, t+after_range) 内的前一个/后一个非空值完成填充。

因为时间区间 [2017-11-07T23:55:00, 2017-11-07T23:57:00) 内均没有数据,所以本例虽然通过设置 before_range 填充了 [2017-11-07T23:50:00, 2017-11-07T23:51:00) 的数据,但由于 before_range 较小,[2017-11-07T23:56:00, 2017-11-07T23:57:00) 的数据无法填充。

before_range, after_range 参数也可辅助 LINEAR 方式进行填充,示例如下:

SELECT last_value(temperature) FROM root.ln.wf01.wt01 GROUP BY([2017-11-07T23:50:00, 2017-11-07T23:59:00),1m) FILL (LINEAR, 5m, 5m);

结果:

IoTDB> SELECT last_value(temperature) FROM root.ln.wf01.wt01 GROUP BY([2017-11-07T23:50:00, 2017-11-07T23:59:00),1m) FILL (LINEAR, 5m, 5m);
+-----------------------------+-----------------------------------------+
|                         Time|last_value(root.ln.wf01.wt01.temperature)|
+-----------------------------+-----------------------------------------+
|2017-11-07T23:50:00.000+08:00|                                22.970001|
|2017-11-07T23:51:00.000+08:00|                                    22.24|
|2017-11-07T23:52:00.000+08:00|                                    23.41|
|2017-11-07T23:53:00.000+08:00|                                    24.58|
|2017-11-07T23:54:00.000+08:00|                                    22.52|
|2017-11-07T23:55:00.000+08:00|                                23.143333|
|2017-11-07T23:56:00.000+08:00|                                23.766666|
|2017-11-07T23:57:00.000+08:00|                                    24.39|
|2017-11-07T23:58:00.000+08:00|                                23.283333|
+-----------------------------+-----------------------------------------+
Total line number = 9
It costs 0.008s

注意:设原始降采样查询区间为 [start_time, end_time),在指定 before_range, after_range 参数后,降采样查询结果不变,但查询区间将转变为 [start_time - before_range, end_time + after_range)。因此这两个参数设置较大时会影响效率,使用时需注意。

Value 填充

值填充方式会将输入的常量值解析为字符串,填充时尝试将字符串常量转换为对应类型的数据,若转换成功则进行填充,否则就不填充。举例如下:

SELECT last_value(temperature) FROM root.ln.wf01.wt01 GROUP BY([2017-11-07T23:50:00, 2017-11-07T23:59:00),1m) FILL (20.0)
SELECT last_value(temperature) FROM root.ln.wf01.wt01 GROUP BY([2017-11-07T23:50:00, 2017-11-07T23:59:00),1m) FILL ('temperature')

结果:

IoTDB> SELECT last_value(temperature) FROM root.ln.wf01.wt01 GROUP BY([2017-11-07T23:50:00, 2017-11-07T23:59:00),1m) FILL (20.0);
+-----------------------------+-----------------------------------------+
|                         Time|last_value(root.ln.wf01.wt01.temperature)|
+-----------------------------+-----------------------------------------+
|2017-11-07T23:50:00.000+08:00|                                     20.0|
|2017-11-07T23:51:00.000+08:00|                                    22.24|
|2017-11-07T23:52:00.000+08:00|                                     20.0|
|2017-11-07T23:53:00.000+08:00|                                    24.58|
|2017-11-07T23:54:00.000+08:00|                                    22.52|
|2017-11-07T23:55:00.000+08:00|                                     20.0|
|2017-11-07T23:56:00.000+08:00|                                     20.0|
|2017-11-07T23:57:00.000+08:00|                                    24.39|
|2017-11-07T23:58:00.000+08:00|                                     20.0|
+-----------------------------+-----------------------------------------+
Total line number = 9
It costs 0.007s

IoTDB> SELECT last_value(temperature) FROM root.ln.wf01.wt01 GROUP BY([2017-11-07T23:50:00, 2017-11-07T23:59:00),1m) FILL ('temperature');
+-----------------------------+-----------------------------------------+
|                         Time|last_value(root.ln.wf01.wt01.temperature)|
+-----------------------------+-----------------------------------------+
|2017-11-07T23:50:00.000+08:00|                                     null|
|2017-11-07T23:51:00.000+08:00|                                    22.24|
|2017-11-07T23:52:00.000+08:00|                                     null|
|2017-11-07T23:53:00.000+08:00|                                    24.58|
|2017-11-07T23:54:00.000+08:00|                                    22.52|
|2017-11-07T23:55:00.000+08:00|                                     null|
|2017-11-07T23:56:00.000+08:00|                                     null|
|2017-11-07T23:57:00.000+08:00|                                    24.39|
|2017-11-07T23:58:00.000+08:00|                                     null|
+-----------------------------+-----------------------------------------+
Total line number = 9
It costs 0.005s

时间区间和路径层级分组聚合查询

除此之外,还可以通过定义 LEVEL 来统计指定层级下的数据点个数。

例如:

统计降采样后的数据点个数

select count(status) from root.ln.wf01.wt01 group by ((2017-11-01T00:00:00, 2017-11-07T23:00:00],1d), level=1;

结果:

+-----------------------------+-------------------------+
|                         Time|COUNT(root.ln.*.*.status)|
+-----------------------------+-------------------------+
|2017-11-02T00:00:00.000+08:00|                     1440|
|2017-11-03T00:00:00.000+08:00|                     1440|
|2017-11-04T00:00:00.000+08:00|                     1440|
|2017-11-05T00:00:00.000+08:00|                     1440|
|2017-11-06T00:00:00.000+08:00|                     1440|
|2017-11-07T00:00:00.000+08:00|                     1440|
|2017-11-07T23:00:00.000+08:00|                     1380|
+-----------------------------+-------------------------+
Total line number = 7
It costs 0.006s

加上滑动 Step 的降采样后的结果也可以汇总

select count(status) from root.ln.wf01.wt01 group by ([0,20),2ms,3ms), level=1;
+-----------------------------+-------------------------+
|                         Time|COUNT(root.ln.*.*.status)|
+-----------------------------+-------------------------+
|2017-11-01T00:00:00.000+08:00|                      180|
|2017-11-02T00:00:00.000+08:00|                      180|
|2017-11-03T00:00:00.000+08:00|                      180|
|2017-11-04T00:00:00.000+08:00|                      180|
|2017-11-05T00:00:00.000+08:00|                      180|
|2017-11-06T00:00:00.000+08:00|                      180|
|2017-11-07T00:00:00.000+08:00|                      180|
+-----------------------------+-------------------------+
Total line number = 7
It costs 0.004s

聚合查询嵌套表达式

IoTDB 支持在 SELECT 字句中执行由聚合查询和其他运算组成的任意嵌套表达式。

示例
  1. 不指定 GROUP BY 的聚合查询。

输入:

select avg(temperature),
       sin(avg(temperature)),
       avg(temperature) + 1,
       -sum(hardware),
       avg(temperature) + sum(hardware)
from root.ln.wf01.wt01;

结果:

+----------------------------------+---------------------------------------+--------------------------------------+--------------------------------+--------------------------------------------------------------------+
|avg(root.ln.wf01.wt01.temperature)|sin(avg(root.ln.wf01.wt01.temperature))|avg(root.ln.wf01.wt01.temperature) + 1|-sum(root.ln.wf01.wt01.hardware)|avg(root.ln.wf01.wt01.temperature) + sum(root.ln.wf01.wt01.hardware)|
+----------------------------------+---------------------------------------+--------------------------------------+--------------------------------+--------------------------------------------------------------------+
|                15.927999999999999|                   -0.21826546964855045|                    16.927999999999997|                         -7426.0|                                                            7441.928|
+----------------------------------+---------------------------------------+--------------------------------------+--------------------------------+--------------------------------------------------------------------+
Total line number = 1
It costs 0.009s

输入:

select count(a),
       count(b),
       ((count(a) + 1) * 2 - 1) % 2 + 1.5,
       -(count(a) + count(b)) * (count(a) * count(b)) + count(a) / count(b)
from root.sg;

结果:

+----------------+----------------+----------------------------------------------+----------------------------------------------------------------------------------------------------------------------+
|count(root.sg.a)|count(root.sg.b)|((((count(root.sg.a) + 1) * 2) - 1) % 2) + 1.5|(-count(root.sg.a) + count(root.sg.b) * (count(root.sg.a) * count(root.sg.b))) + (count(root.sg.a) / count(root.sg.b))|
+----------------+----------------+----------------------------------------------+----------------------------------------------------------------------------------------------------------------------+
|               4|               3|                                           2.5|                                                                                                    -82.66666666666667|
+----------------+----------------+----------------------------------------------+----------------------------------------------------------------------------------------------------------------------+
Total line number = 1
It costs 0.013s
  1. 指定 GROUP BY 的聚合查询。

输入:

select avg(temperature),
       sin(avg(temperature)),
       avg(temperature) + 1,
       -sum(hardware),
       avg(temperature) + sum(hardware) as custom_sum
from root.ln.wf01.wt01
GROUP BY([10, 90), 10ms);

结果:

+-----------------------------+----------------------------------+---------------------------------------+--------------------------------------+--------------------------------+----------+
|                         Time|avg(root.ln.wf01.wt01.temperature)|sin(avg(root.ln.wf01.wt01.temperature))|avg(root.ln.wf01.wt01.temperature) + 1|-sum(root.ln.wf01.wt01.hardware)|custom_sum|
+-----------------------------+----------------------------------+---------------------------------------+--------------------------------------+--------------------------------+----------+
|1970-01-01T08:00:00.010+08:00|                13.987499999999999|                     0.9888207947857667|                    14.987499999999999|                         -3211.0| 3224.9875|
|1970-01-01T08:00:00.020+08:00|                              29.6|                    -0.9701057337071853|                                  30.6|                         -3720.0|    3749.6|
|1970-01-01T08:00:00.030+08:00|                              null|                                   null|                                  null|                            null|      null|
|1970-01-01T08:00:00.040+08:00|                              null|                                   null|                                  null|                            null|      null|
|1970-01-01T08:00:00.050+08:00|                              null|                                   null|                                  null|                            null|      null|
|1970-01-01T08:00:00.060+08:00|                              null|                                   null|                                  null|                            null|      null|
|1970-01-01T08:00:00.070+08:00|                              null|                                   null|                                  null|                            null|      null|
|1970-01-01T08:00:00.080+08:00|                              null|                                   null|                                  null|                            null|      null|
+-----------------------------+----------------------------------+---------------------------------------+--------------------------------------+--------------------------------+----------+
Total line number = 8
It costs 0.012s
注意

目前此功能尚不支持填充算子(FILL)和按层级聚合(GROUP BY LEVEL)查询, 在后续版本会支持。

聚合计算目前只能当做最底层表达式输入,暂不支持聚合函数内部出现表达式。

即不支持以下查询

SELECT avg(s1+1) FROM root.sg.d1; -- 聚合函数内部有表达式
SELECT avg(s1) + avg(s2) FROM root.sg.* GROUP BY LEVEL=1; -- 按层级聚合
SELECT avg(s1) + avg(s2) FROM root.sg.d1 GROUP BY([0, 10000), 1s) FILL(previous); -- 空值填充 

最新点查询

SQL 语法:

select last <Path> [COMMA <Path>]* from < PrefixPath > [COMMA < PrefixPath >]* <WhereClause>

其含义是:查询时间序列 prefixPath.path 中最近时间戳的数据

<WhereClause>中当前只支持含有'>‘或’>='的时间过滤条件,任何其他过滤条件都将会返回异常。

结果集为四列的结构

+----+----------+-----+--------+
|Time|timeseries|value|dataType|
+----+----------+-----+--------+

示例 1:查询 root.ln.wf01.wt01.status 的最新数据点

IoTDB> select last status from root.ln.wf01.wt01
+-----------------------------+------------------------+-----+--------+
|                         Time|              timeseries|value|dataType|
+-----------------------------+------------------------+-----+--------+
|2017-11-07T23:59:00.000+08:00|root.ln.wf01.wt01.status|false| BOOLEAN|
+-----------------------------+------------------------+-----+--------+
Total line number = 1
It costs 0.000s

示例 2:查询 root.ln.wf01.wt01 下 status,temperature 时间戳大于等于 2017-11-07T23:50:00 的最新数据点。

IoTDB> select last status, temperature from root.ln.wf01.wt01 where time >= 2017-11-07T23:50:00
+-----------------------------+-----------------------------+---------+--------+
|                         Time|                   timeseries|    value|dataType|
+-----------------------------+-----------------------------+---------+--------+
|2017-11-07T23:59:00.000+08:00|     root.ln.wf01.wt01.status|    false| BOOLEAN|
|2017-11-07T23:59:00.000+08:00|root.ln.wf01.wt01.temperature|21.067368|  DOUBLE|
+-----------------------------+-----------------------------+---------+--------+
Total line number = 2
It costs 0.002s

模糊查询

模糊查询分为Like语句和Regexp语句,都可以支持对Text类型的数据进行模糊匹配

Like语句:

示例 1:查询 root.sg.devicevalue 含有'cc'的数据。 % 表示任意0个或多个字符。

IoTDB> select * from root.sg.device where value like '%cc%'
+-----------------------------+--------------------+
|                         Time|root.sg.device.value|
+-----------------------------+--------------------+
|2017-11-07T23:59:00.000+08:00|            aabbccdd| 
|2017-11-07T23:59:00.000+08:00|                  cc|
+-----------------------------+--------------------+
Total line number = 2
It costs 0.002s

示例 2:查询 root.sg.devicevalue 中间为 'b'、前后为任意单个字符的数据。 _ 表示任意单个字符。

IoTDB> select * from root.sg.device where value like '_b_'
+-----------------------------+--------------------+
|                         Time|root.sg.device.value|
+-----------------------------+--------------------+
|2017-11-07T23:59:00.000+08:00|                 abc| 
+-----------------------------+--------------------+
Total line number = 1
It costs 0.002s

Regexp语句:

需要传入的过滤条件为 Java 标准库风格的正则表达式

示例 1:查询 root.sg.device 下 value 值为26个英文字符组成的字符串

IoTDB> select * from root.sg.device where value regexp '^[A-Za-z]+$'
+-----------------------------+--------------------+
|                         Time|root.sg.device.value|
+-----------------------------+--------------------+
|2017-11-07T23:59:00.000+08:00|            aabbccdd| 
|2017-11-07T23:59:00.000+08:00|                  cc|
+-----------------------------+--------------------+
Total line number = 2
It costs 0.002s

示例 2:查询 root.sg.device 下 value 值为26个小写英文字符组成的字符串 且时间大于100的

IoTDB> select * from root.sg.device where value regexp '^[a-z]+$' and time > 100
+-----------------------------+--------------------+
|                         Time|root.sg.device.value|
+-----------------------------+--------------------+
|2017-11-07T23:59:00.000+08:00|            aabbccdd| 
|2017-11-07T23:59:00.000+08:00|                  cc|
+-----------------------------+--------------------+
Total line number = 2
It costs 0.002s

常见的正则匹配举例:

长度为3-20的所有字符:^.{3,20}$
大写英文字符:^[A-Z]+$
数字和英文字符:^[A-Za-z0-9]+$
以a开头的:^a.*

更多语法请参照 SQL REFERENCE.

别名

由于 IoTDB 独特的数据模型,在每个传感器前都附带有设备等诸多额外信息。有时,我们只针对某个具体设备查询,而这些前缀信息频繁显示造成了冗余,影响了结果集的显示与分析。这时我们可以使用 IoTDB 提供的 AS 函数,将查询中出现的时间序列给定一个别名。

例如:

select s1 as temperature, s2 as speed from root.ln.wf01.wt01;

则结果集将显示为:

Timetemperaturespeed
.........

结果集行列输出控制 (LIMIT & OFFSET)

IoTDB 提供 LIMIT/SLIMIT 子句和 OFFSET/SOFFSET 子句,以使用户可以更好地控制查询结果。使用 LIMIT 和 SLIMIT 子句可让用户控制查询结果的行数和列数, 并且使用 OFFSET 和 SOFSET 子句允许用户设置结果显示的起始位置。

请注意,按组查询不支持 LIMIT 和 OFFSET。

本章主要介绍查询结果的行和列控制的相关示例。你还可以使用 Java JDBC 标准接口执行查询。

行控制

通过使用 LIMIT 和 OFFSET 子句,用户可以以与行相关的方式控制查询结果。 我们将通过以下示例演示如何使用 LIMIT 和 OFFSET 子句。

  • 示例 1:基本的 LIMIT 子句

SQL 语句是:

select status, temperature from root.ln.wf01.wt01 limit 10

意思是:

所选设备为 ln 组 wf01 工厂 wt01 设备; 选择的时间序列是“状态”和“温度”。 SQL 语句要求返回查询结果的前 10 行。

结果如下所示:

+-----------------------------+------------------------+-----------------------------+
|                         Time|root.ln.wf01.wt01.status|root.ln.wf01.wt01.temperature|
+-----------------------------+------------------------+-----------------------------+
|2017-11-01T00:00:00.000+08:00|                    true|                        25.96|
|2017-11-01T00:01:00.000+08:00|                    true|                        24.36|
|2017-11-01T00:02:00.000+08:00|                   false|                        20.09|
|2017-11-01T00:03:00.000+08:00|                   false|                        20.18|
|2017-11-01T00:04:00.000+08:00|                   false|                        21.13|
|2017-11-01T00:05:00.000+08:00|                   false|                        22.72|
|2017-11-01T00:06:00.000+08:00|                   false|                        20.71|
|2017-11-01T00:07:00.000+08:00|                   false|                        21.45|
|2017-11-01T00:08:00.000+08:00|                   false|                        22.58|
|2017-11-01T00:09:00.000+08:00|                   false|                        20.98|
+-----------------------------+------------------------+-----------------------------+
Total line number = 10
It costs 0.000s
  • 示例 2:带 OFFSET 的 LIMIT 子句

SQL 语句是:

select status, temperature from root.ln.wf01.wt01 limit 5 offset 3

意思是:

所选设备为 ln 组 wf01 工厂 wt01 设备; 选择的时间序列是“状态”和“温度”。 SQL 语句要求返回查询结果的第 3 至 7 行(第一行编号为 0 行)。

结果如下所示:

+-----------------------------+------------------------+-----------------------------+
|                         Time|root.ln.wf01.wt01.status|root.ln.wf01.wt01.temperature|
+-----------------------------+------------------------+-----------------------------+
|2017-11-01T00:03:00.000+08:00|                   false|                        20.18|
|2017-11-01T00:04:00.000+08:00|                   false|                        21.13|
|2017-11-01T00:05:00.000+08:00|                   false|                        22.72|
|2017-11-01T00:06:00.000+08:00|                   false|                        20.71|
|2017-11-01T00:07:00.000+08:00|                   false|                        21.45|
+-----------------------------+------------------------+-----------------------------+
Total line number = 5
It costs 0.342s
  • 示例 3:LIMIT 子句与 WHERE 子句结合

SQL 语句是:

select status,temperature from root.ln.wf01.wt01 where time > 2017-11-01T00:05:00.000 and time< 2017-11-01T00:12:00.000 limit 5 offset 3

意思是:

所选设备为 ln 组 wf01 工厂 wt01 设备; 选择的时间序列是“状态”和“温度”。 SQL 语句要求返回时间“ 2017-11-01T00:05:00.000”和“ 2017-11-01T00:12:00.000”之间的状态和温度传感器值的第 3 至 4 行(第一行) 编号为第 0 行)。

结果如下所示:

+-----------------------------+------------------------+-----------------------------+
|                         Time|root.ln.wf01.wt01.status|root.ln.wf01.wt01.temperature|
+-----------------------------+------------------------+-----------------------------+
|2017-11-01T00:03:00.000+08:00|                   false|                        20.18|
|2017-11-01T00:04:00.000+08:00|                   false|                        21.13|
|2017-11-01T00:05:00.000+08:00|                   false|                        22.72|
|2017-11-01T00:06:00.000+08:00|                   false|                        20.71|
|2017-11-01T00:07:00.000+08:00|                   false|                        21.45|
+-----------------------------+------------------------+-----------------------------+
Total line number = 5
It costs 0.000s
  • 示例 4:LIMIT 子句与 GROUP BY 子句组合

SQL 语句是:

select count(status), max_value(temperature) from root.ln.wf01.wt01 group by ([2017-11-01T00:00:00, 2017-11-07T23:00:00),1d) limit 4 offset 3

意思是:

SQL 语句子句要求返回查询结果的第 3 至 6 行(第一行编号为 0 行)。

结果如下所示:

+-----------------------------+-------------------------------+----------------------------------------+
|                         Time|count(root.ln.wf01.wt01.status)|max_value(root.ln.wf01.wt01.temperature)|
+-----------------------------+-------------------------------+----------------------------------------+
|2017-11-04T00:00:00.000+08:00|                           1440|                                    26.0|
|2017-11-05T00:00:00.000+08:00|                           1440|                                    26.0|
|2017-11-06T00:00:00.000+08:00|                           1440|                                   25.99|
|2017-11-07T00:00:00.000+08:00|                           1380|                                    26.0|
+-----------------------------+-------------------------------+----------------------------------------+
Total line number = 4
It costs 0.016s

值得注意的是,由于当前的 FILL 子句只能在某个时间点填充时间序列的缺失值,也就是说,FILL 子句的执行结果恰好是一行,因此 LIMIT 和 OFFSET 不会是 与 FILL 子句结合使用,否则将提示错误。 例如,执行以下 SQL 语句:

select temperature from root.sgcc.wf03.wt01 where time = 2017-11-01T16:37:50.000 fill(previous, 1m) limit 10

SQL 语句将不会执行,并且相应的错误提示如下:

Msg: 401: Error occured while parsing SQL to physical plan: line 1:101 mismatched input 'limit' expecting {<EOF>, ';'}

列控制

通过使用 SLIMIT 和 SOFFSET 子句,用户可以与列相关的方式控制查询结果。 我们将通过以下示例演示如何使用 SLIMIT 和 SOFFSET 子句。

  • 示例 1:基本的 SLIMIT 子句

SQL 语句是:

select * from root.ln.wf01.wt01 where time > 2017-11-01T00:05:00.000 and time < 2017-11-01T00:12:00.000 slimit 1

意思是:

所选设备为 ln 组 wf01 工厂 wt01 设备; 所选时间序列是该设备下的第二列,即温度。 SQL 语句要求在“2017-11-01T00:05:00.000”和“2017-11-01T00:12:00.000”的时间点之间选择温度传感器值。

结果如下所示:

+-----------------------------+-----------------------------+
|                         Time|root.ln.wf01.wt01.temperature|
+-----------------------------+-----------------------------+
|2017-11-01T00:06:00.000+08:00|                        20.71|
|2017-11-01T00:07:00.000+08:00|                        21.45|
|2017-11-01T00:08:00.000+08:00|                        22.58|
|2017-11-01T00:09:00.000+08:00|                        20.98|
|2017-11-01T00:10:00.000+08:00|                        25.52|
|2017-11-01T00:11:00.000+08:00|                        22.91|
+-----------------------------+-----------------------------+
Total line number = 6
It costs 0.000s
  • 示例 2:带 SOFFSET 的 SLIMIT 子句

SQL 语句是:

select * from root.ln.wf01.wt01 where time > 2017-11-01T00:05:00.000 and time < 2017-11-01T00:12:00.000 slimit 1 soffset 1

意思是:

所选设备为 ln 组 wf01 工厂 wt01 设备; 所选时间序列是该设备下的第一列,即电源状态。 SQL 语句要求在" 2017-11-01T00:05:00.000“和”2017-11-01T00:12:00.000"的时间点之间选择状态传感器值。

结果如下所示:

+-----------------------------+------------------------+
|                         Time|root.ln.wf01.wt01.status|
+-----------------------------+------------------------+
|2017-11-01T00:06:00.000+08:00|                   false|
|2017-11-01T00:07:00.000+08:00|                   false|
|2017-11-01T00:08:00.000+08:00|                   false|
|2017-11-01T00:09:00.000+08:00|                   false|
|2017-11-01T00:10:00.000+08:00|                    true|
|2017-11-01T00:11:00.000+08:00|                   false|
+-----------------------------+------------------------+
Total line number = 6
It costs 0.003s
  • 示例 3:SLIMIT 子句与 GROUP BY 子句结合

SQL 语句是:

select max_value(*) from root.ln.wf01.wt01 group by ([2017-11-01T00:00:00, 2017-11-07T23:00:00),1d) slimit 1 soffset 1

结果如下所示:

+-----------------------------+-----------------------------------+
|                         Time|max_value(root.ln.wf01.wt01.status)|
+-----------------------------+-----------------------------------+
|2017-11-01T00:00:00.000+08:00|                               true|
|2017-11-02T00:00:00.000+08:00|                               true|
|2017-11-03T00:00:00.000+08:00|                               true|
|2017-11-04T00:00:00.000+08:00|                               true|
|2017-11-05T00:00:00.000+08:00|                               true|
|2017-11-06T00:00:00.000+08:00|                               true|
|2017-11-07T00:00:00.000+08:00|                               true|
+-----------------------------+-----------------------------------+
Total line number = 7
It costs 0.000s
  • 示例 4:SLIMIT 子句与 FILL 子句结合

SQL 语句是:

select * from root.sgcc.wf03.wt01 where time = 2017-11-01T16:37:50.000 fill(previous, 1m) slimit 1 soffset 1

结果如下所示:

+-----------------------------+--------------------------+
|                         Time|root.sgcc.wf03.wt01.status|
+-----------------------------+--------------------------+
|2017-11-01T16:35:00.000+08:00|                      true|
+-----------------------------+--------------------------+
Total line number = 1
It costs 0.007s

行和列控制的组合

除了对查询结果进行行或列控制之外,IoTDB 还允许用户控制查询结果的行和列。 这是同时包含 LIMIT 子句和 SLIMIT 子句的完整示例。

SQL 语句是:

select * from root.ln.wf01.wt01 limit 10 offset 100 slimit 2 soffset 0

意思是:

所选设备为 ln 组 wf01 工厂 wt01 设备; 所选时间序列是此设备下的第 0 列至第 1 列(第一列编号为第 0 列)。 SQL 语句子句要求返回查询结果的第 100 至 109 行(第一行编号为 0 行)。

结果如下所示:

+-----------------------------+-----------------------------+------------------------+
|                         Time|root.ln.wf01.wt01.temperature|root.ln.wf01.wt01.status|
+-----------------------------+-----------------------------+------------------------+
|2017-11-01T01:40:00.000+08:00|                        21.19|                   false|
|2017-11-01T01:41:00.000+08:00|                        22.79|                   false|
|2017-11-01T01:42:00.000+08:00|                        22.98|                   false|
|2017-11-01T01:43:00.000+08:00|                        21.52|                   false|
|2017-11-01T01:44:00.000+08:00|                        23.45|                    true|
|2017-11-01T01:45:00.000+08:00|                        24.06|                    true|
|2017-11-01T01:46:00.000+08:00|                         22.6|                   false|
|2017-11-01T01:47:00.000+08:00|                        23.78|                    true|
|2017-11-01T01:48:00.000+08:00|                        24.72|                    true|
|2017-11-01T01:49:00.000+08:00|                        24.68|                    true|
+-----------------------------+-----------------------------+------------------------+
Total line number = 10
It costs 0.009s

错误处理

当 LIMIT / SLIMIT 的参数 N / SN 超过结果集的大小时,IoTDB 将按预期返回所有结果。 例如,原始 SQL 语句的查询结果由六行组成,我们通过 LIMIT 子句选择前 100 行:

select status,temperature from root.ln.wf01.wt01 where time > 2017-11-01T00:05:00.000 and time < 2017-11-01T00:12:00.000 limit 100

结果如下所示:

+-----------------------------+------------------------+-----------------------------+
|                         Time|root.ln.wf01.wt01.status|root.ln.wf01.wt01.temperature|
+-----------------------------+------------------------+-----------------------------+
|2017-11-01T00:06:00.000+08:00|                   false|                        20.71|
|2017-11-01T00:07:00.000+08:00|                   false|                        21.45|
|2017-11-01T00:08:00.000+08:00|                   false|                        22.58|
|2017-11-01T00:09:00.000+08:00|                   false|                        20.98|
|2017-11-01T00:10:00.000+08:00|                    true|                        25.52|
|2017-11-01T00:11:00.000+08:00|                   false|                        22.91|
+-----------------------------+------------------------+-----------------------------+
Total line number = 6
It costs 0.005s

当 LIMIT / SLIMIT 子句的参数 N / SN 超过允许的最大值(N / SN 的类型为 int32)时,系统将提示错误。 例如,执行以下 SQL 语句:

select status,temperature from root.ln.wf01.wt01 where time > 2017-11-01T00:05:00.000 and time < 2017-11-01T00:12:00.000 limit 1234567890123456789

SQL 语句将不会执行,并且相应的错误提示如下:

Msg: 303: check metadata error: Out of range. LIMIT <N>: N should be Int32.

当 LIMIT / LIMIT 子句的参数 N / SN 不是正整数时,系统将提示错误。 例如,执行以下 SQL 语句:

select status,temperature from root.ln.wf01.wt01 where time > 2017-11-01T00:05:00.000 and time < 2017-11-01T00:12:00.000 limit 13.1

SQL 语句将不会执行,并且相应的错误提示如下:

Msg: 401: line 1:129 mismatched input '.' expecting {<EOF>, ';'}

当 LIMIT 子句的参数 OFFSET 超过结果集的大小时,IoTDB 将返回空结果集。 例如,执行以下 SQL 语句:

select status,temperature from root.ln.wf01.wt01 where time > 2017-11-01T00:05:00.000 and time < 2017-11-01T00:12:00.000 limit 2 offset 6

结果如下所示:

+----+------------------------+-----------------------------+
|Time|root.ln.wf01.wt01.status|root.ln.wf01.wt01.temperature|
+----+------------------------+-----------------------------+
Empty set.
It costs 0.005s

结果集空值过滤

  • 如果结果集中,任意一列为 null,则过滤掉该行;即获得的结果集不包含任何空值
select * from root.ln.** where time <= 2017-11-01T00:01:00 WITHOUT NULL ANY
  • 在降采样查询中,如果结果集的某一行所有列都为 null,则过滤掉该行;即获得的结果集不包含所有值都为 null 的行
select * from root.ln.** where time <= 2017-11-01T00:01:00 WITHOUT NULL ALL

其他对齐方式的结果集

IoTDB 支持另外两种结果返回形式:按设备时间对齐 ‘align by device’ 和 时序不对齐 ‘disable align’.

align by device

‘align by device’ 对齐方式下,设备 ID 会单独作为一列出现。在 select 子句中写了多少列,最终结果就会有该列数+2 (时间列和设备名字列)。SQL 形如:

select * from root.ln.** where time <= 2017-11-01T00:01:00 align by device

结果如下:

+-----------------------------+-----------------+-----------+------+--------+
|                         Time|           Device|temperature|status|hardware|
+-----------------------------+-----------------+-----------+------+--------+
|2017-11-01T00:00:00.000+08:00|root.ln.wf01.wt01|      25.96|  true|    null|
|2017-11-01T00:01:00.000+08:00|root.ln.wf01.wt01|      24.36|  true|    null|
|1970-01-01T08:00:00.001+08:00|root.ln.wf02.wt02|       null|  true|      v1|
|1970-01-01T08:00:00.002+08:00|root.ln.wf02.wt02|       null| false|      v2|
|2017-11-01T00:00:00.000+08:00|root.ln.wf02.wt02|       null|  true|      v2|
|2017-11-01T00:01:00.000+08:00|root.ln.wf02.wt02|       null|  true|      v2|
+-----------------------------+-----------------+-----------+------+--------+
Total line number = 6
It costs 0.012s

disable align

‘disable align’ 意味着每条时序就有 2 列存在。Disable Align 只能用于查询语句句尾,不能用于聚合查询、Fill 语句、Group by 或 Group by device 语句,但可用于 Limit 语句。结果显示若无数据显示为空白。

SQL 形如:

select * from root.ln.** where time <= 2017-11-01T00:01:00 disable align

结果如下:

+-----------------------------+--------------------------+-----------------------------+------------------------+-----------------------------+-----------------------------+-----------------------------+------------------------+
|                         Time|root.ln.wf02.wt02.hardware|                         Time|root.ln.wf02.wt02.status|                         Time|root.ln.wf01.wt01.temperature|                         Time|root.ln.wf01.wt01.status|
+-----------------------------+--------------------------+-----------------------------+------------------------+-----------------------------+-----------------------------+-----------------------------+------------------------+
|1970-01-01T08:00:00.001+08:00|                        v1|1970-01-01T08:00:00.001+08:00|                    true|2017-11-01T00:00:00.000+08:00|                        25.96|2017-11-01T00:00:00.000+08:00|                    true|
|1970-01-01T08:00:00.002+08:00|                        v2|1970-01-01T08:00:00.002+08:00|                   false|2017-11-01T00:01:00.000+08:00|                        24.36|2017-11-01T00:01:00.000+08:00|                    true|
|2017-11-01T00:00:00.000+08:00|                        v2|2017-11-01T00:00:00.000+08:00|                    true|                         null|                         null|                         null|                    null|
|2017-11-01T00:01:00.000+08:00|                        v2|2017-11-01T00:01:00.000+08:00|                    true|                         null|                         null|                         null|                    null|
+-----------------------------+--------------------------+-----------------------------+------------------------+-----------------------------+-----------------------------+-----------------------------+------------------------+
Total line number = 4
It costs 0.018s

更多语法请参照 SQL REFERENCE

数据删除

用户使用 DELETE 语句 可以删除指定的时间序列中符合时间删除条件的数据。在删除数据时,用户可以选择需要删除的一个或多个时间序列、时间序列的前缀、时间序列带、*路径对某一个时间区间内的数据进行删除。

在 JAVA 编程环境中,您可以使用 JDBC API 单条或批量执行 DELETE 语句。

单传感器时间序列值删除

以测控 ln 集团为例,存在这样的使用场景:

wf02 子站的 wt02 设备在 2017-11-01 16:26:00 之前的供电状态出现多段错误,且无法分析其正确数据,错误数据影响了与其他设备的关联分析。此时,需要将此时间段前的数据删除。进行此操作的 SQL 语句为:

delete from root.ln.wf02.wt02.status where time<=2017-11-01T16:26:00;

如果我们仅仅想要删除 2017 年内的在 2017-11-01 16:26:00 之前的数据,可以使用以下 SQL:

delete from root.ln.wf02.wt02.status where time>=2017-01-01T00:00:00 and time<=2017-11-01T16:26:00;

IoTDB 支持删除一个时间序列任何一个时间范围内的所有时序点,用户可以使用以下 SQL 语句指定需要删除的时间范围:

delete from root.ln.wf02.wt02.status where time < 10
delete from root.ln.wf02.wt02.status where time <= 10
delete from root.ln.wf02.wt02.status where time < 20 and time > 10
delete from root.ln.wf02.wt02.status where time <= 20 and time >= 10
delete from root.ln.wf02.wt02.status where time > 20
delete from root.ln.wf02.wt02.status where time >= 20
delete from root.ln.wf02.wt02.status where time = 20

需要注意,当前的删除语句不支持 where 子句后的时间范围为多个由 OR 连接成的时间区间。如下删除语句将会解析出错:

delete from root.ln.wf02.wt02.status where time > 4 or time < 0
Msg: 303: Check metadata error: For delete statement, where clause can only contain atomic
expressions like : time > XXX, time <= XXX, or two atomic expressions connected by 'AND'

如果 delete 语句中未指定 where 子句,则会删除时间序列中的所有数据。

delete from root.ln.wf02.status

多传感器时间序列值删除

当 ln 集团 wf02 子站的 wt02 设备在 2017-11-01 16:26:00 之前的供电状态和设备硬件版本都需要删除,此时可以使用含义更广的 路径模式(Path Pattern) 进行删除操作,进行此操作的 SQL 语句为:

delete from root.ln.wf02.wt02.* where time <= 2017-11-01T16:26:00;

需要注意的是,当删除的路径不存在时,IoTDB 不会提示路径不存在,而是显示执行成功,因为 SQL 是一种声明式的编程方式,除非是语法错误、权限不足等,否则都不认为是错误,如下所示。

IoTDB> delete from root.ln.wf03.wt02.status where time < now()
Msg: TimeSeries does not exist and its data cannot be deleted

删除时间分区 (实验性功能)

您可以通过如下语句来删除某一个存储组下的指定时间分区:

DELETE PARTITION root.ln 0,1,2

上例中的 0,1,2 为待删除时间分区的 id,您可以通过查看 IoTDB 的数据文件夹找到它,或者可以通过计算timestamp / partitionInterval(向下取整), 手动地将一个时间戳转换为对应的 id,其中的partitionInterval可以在 IoTDB 的配置文件中找到(如果您使用的版本支持时间分区)。

请注意该功能目前只是实验性的,如果您不是开发者,使用时请务必谨慎。