blob: 3316d172ae819bf88cd37e05bf64c372414296b7 [file] [log] [blame] [view]
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
## 数据操作语言(DML)
### 数据写入
IoTDB为用户提供多种插入实时数据的方式,例如在[Cli/Shell工具](../CLI/Command-Line-Interface.md)中直接输入插入数据的INSERT语句,或使用Java API(标准[Java JDBC](../API/Programming-JDBC.md)接口)单条或批量执行插入数据的INSERT语句。
本节主要为您介绍实时数据接入的INSERT语句在场景中的实际使用示例,有关INSERT SQL语句的详细语法请参见本文[INSERT语句](../Appendix/SQL-Reference.md)节。
#### 使用INSERT语句
使用INSERT语句可以向指定的已经创建的一条或多条时间序列中插入数据。对于每一条数据,均由一个时间戳类型的时间戳和一个数值或布尔值、字符串类型的传感器采集值组成。
在本节的场景实例下,以其中的两个时间序列`root.ln.wf02.wt02.status`和`root.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')
```
插入数据后我们可以使用SELECT语句简单查询已插入的数据。
```
IoTDB > select * from root.ln.wf02 where time < 3
```
结果如图所示。由查询结果可以看出,单列、多列数据的插入操作正确执行。
```
+-----------------------------+--------------------------+------------------------+
| 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|
+-----------------------------+--------------------------+------------------------+
Total line number = 2
It costs 0.170s
```
### 数据查询
#### 时间条件过滤查询
本节主要介绍时间切片查询的相关示例,主要使用的是[IoTDB SELECT语句](../Appendix/SQL-Reference.md)。同时,您也可以使用[Java JDBC](../API/Programming-JDBC.md)标准接口来执行相关的查询语句。
* 根据一个时间区间选择一列数据
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节](../Data-Concept/Data-Model-and-Terminology.md))时间点以前的所有温度传感器的值。
该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语句为:
```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](../Appendix/SQL-Reference.md).
* IoTDB支持另外两种结果返回形式: 按设备时间对齐 'align by device' 和 时序不对齐 'disable align'.
'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' 意味着每条时序就有 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](../Appendix/SQL-Reference.md)。
#### 结果空值过滤
* 如果结果集中,任意一列为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 SELECT语句的聚合查询函数。
* 统计总点数
```
select count(status) from root.ln.wf01.wt01;
```
结果:
```
+-------------------------------+
|count(root.ln.wf01.wt01.status)|
+-------------------------------+
| 10080|
+-------------------------------+
Total line number = 1
It costs 0.016s
```
#### 时间区间分组聚合
本章节主要介绍时间区间分组聚合查询的相关示例,
主要使用的是IoTDB SELECT语句的[GROUP BY子句](../Appendix/SQL-Reference.md),
该子句是IoTDB中用于根据用户给定划分条件对结果集进行划分,并对已划分的结果集进行聚合计算的语句。
IoTDB支持根据时间间隔和自定义的滑动步长(默认值与时间间隔相同,自定义的值必须大于等于时间间隔)对结果集进行划分,默认结果按照时间升序排列。
同时,您也可以使用Java JDBC标准接口来执行相关的查询语句。
GROUP BY语句为用户提供三类指定参数:
* 参数1:时间轴显示时间窗参数
* 参数2:划分时间轴的时间间隔参数(必须为正数)
* 参数3:滑动步长(可选参数,默认值与时间间隔相同,自定义的值必须大于等于时间间隔)
三类参数的实际含义已经在下图中指出,这三类参数里,第三个参数是可选的。
接下来,我们将给出三种典型的降频聚合查询的例子:
滑动步长未指定,
指定滑动步长,
带值过滤条件。
<img style="width:100%; max-width:800px; max-height:600px; margin-left:auto; margin-right:auto; display:block;" src="https://user-images.githubusercontent.com/16079446/69109512-f808bc80-0ab2-11ea-9e4d-b2b2f58fb474.png">
* 未指定滑动步长的降频聚合查询
对应的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
```
#### 路径层级分组聚合
在时间序列层级结构中,分层聚合查询用于对某一层级进行聚合查询。
这里使用LEVEL来统计指定层级下的聚合范围,该语句约定root为第0层序列,若统计"root.ln"下所有序列则需指定level为1。
例如:在"root.ln.wf01"下存在多个子序列:wt01,wt02,wt03等均有名为status的序列,
如果需要统计这些子序列的status包含的点个数,使用以下查询:
```
select count(status) from root.ln.wf01.* group by level=2
```
运行结果为:
```
+----------------------------+
|count(root.ln.wf01.*.status)|
+----------------------------+
| 10080|
+----------------------------+
Total line number = 1
It costs 0.003s
```
假设此时添加两条序列,"root.ln.wf01.wt01.temperature" and "root.ln.wf02.wt01.temperature"。
需要同时查询"root.ln.\*.\*.temperature"在第二层级的count聚合结果和sum聚合结果,可以使用下列查询语句:
```
select count(temperature), sum(temperature) from root.ln.*.* group by level=2
```
运行结果:
```
+---------------------------------+---------------------------------+-------------------------------+-------------------------------+
|count(root.ln.wf02.*.temperature)|count(root.ln.wf01.*.temperature)|sum(root.ln.wf02.*.temperature)|sum(root.ln.wf01.*.temperature)|
+---------------------------------+---------------------------------+-------------------------------+-------------------------------+
| 8| 4| 228.0| 91.83000183105469|
+---------------------------------+---------------------------------+-------------------------------+-------------------------------+
Total line number = 1
It costs 0.013s
```
若统计"root.ln.\*.\*"下第一层级的count聚合结果和sum聚合结果,则设置level=1即可:
```
select count(temperature), sum(temperature) from root.ln.*.* group by level=1
```
运行结果:
```
+------------------------------+----------------------------+
|count(root.ln.*.*.temperature)|sum(root.ln.*.*.temperature)|
+------------------------------+----------------------------+
| 12| 319.8300018310547|
+------------------------------+----------------------------+
Total line number = 1
It costs 0.013s
```
分层聚合查询也可被用于其他聚合函数,当前所支持的聚合函数为:count, sum, avg, last_value, first_value, min_time, max_time, min_value, max_value
对于sum, avg, min_value, max_value四种聚合函数,需保证所有聚合的时间序列数据类型相同。其他聚合函数没有此限制。
#### 时间区间和路径层级分组聚合查询
除此之外,还可以通过定义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
```
#### 最新点查询
SQL语法:
```
select last <Path> [COMMA <Path>]* from < PrefixPath > [COMMA < PrefixPath >]* <WhereClause>
```
其含义是:查询时间序列prefixPath.path中最近时间戳的数据
\<WhereClause\>中当前只支持含有'>'或'>='的时间过滤条件,任何其他过滤条件都将会返回异常。
结果集为三列的结构
```
+----+----------+-----+
|Time|timeseries|value|
+----+----------+-----+
```
示例 1:查询 root.ln.wf01.wt01.status 的最新数据点
```
IoTDB> select last status from root.ln.wf01.wt01
+-----------------------------+------------------------+-----+
| Time| timeseries|value|
+-----------------------------+------------------------+-----+
|2017-11-07T23:59:00.000+08:00|root.ln.wf01.wt01.status|false|
+-----------------------------+------------------------+-----+
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|
+-----------------------------+-----------------------------+---------+
|2017-11-07T23:59:00.000+08:00| root.ln.wf01.wt01.status| false|
|2017-11-07T23:59:00.000+08:00|root.ln.wf01.wt01.temperature|21.067368|
+-----------------------------+-----------------------------+---------+
Total line number = 2
It costs 0.002s
```
#### 空值填充
在IoTDB的实际使用中,当进行时间序列的查询操作时,可能会出现在某些时间点值为null的情况,这会妨碍用户进行进一步的分析。 为了更好地反映数据更改的程度,用户希望可以自动填充缺失值。 因此,IoTDB系统引入了自动填充功能。
自动填充功能是指对单列或多列执行时间序列查询时,根据用户指定的方法和有效时间范围填充空值。 如果查询点的值不为null,则填充功能将不起作用。
> 注意:在当前版本中,IoTDB为用户提供两种方法:Previous 和Linear。 Previous 方法用前一个值填充空白。 Linear方法通过线性拟合来填充空白。 并且填充功能只能在执行时间点查询时使用。
* 填充功能
- Previous功能
当查询的时间戳值为空时,将使用前一个时间戳的值来填充空白。 形式化的先前方法如下(有关详细语法,请参见第7.1.3.6节):
```
select <path> from <prefixPath> where time = <T> fill(<data_type>[previous, <before_range>], …)
```
表3-4给出了所有参数的详细说明。
<center>**表3-4previous填充参数列表**
| 参数名称(不区分大小写) | 解释 |
| :----------------------- | :----------------------------------------------------------- |
| path, prefixPath | 查询路径; 必填项 |
| T | 查询时间戳(只能指定一个); 必填项 |
| data\_type | 填充方法使用的数据类型。 可选值是int32,int64,float,double,boolean,text; 可选字段 |
| before\_range | 表示前一种方法的有效时间范围。 当[T-before \_range,T]范围内的值存在时,前一种方法将起作用。 如果未指定before_range,则before_range会使用默认值default_fill_interval; -1表示无穷大; 可选字段 |
</center>
在这里,我们举一个使用先前方法填充空值的示例。 SQL语句如下:
```
select temperature from root.sgcc.wf03.wt01 where time = 2017-11-01T16:37:50.000 fill(float[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]范围)进行填充和显示。
在[样例数据中](https://github.com/thulab/iotdb/files/4438687/OtherMaterial-Sample.Data.txt), 该语句的执行结果如下所示:
```
+-----------------------------+-------------------------------+
| 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(float[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(<data_type>[linear, <before_range>, <after_range>]…)
```
表3-5中给出了所有参数的详细说明。
<center>**表3-5线性填充参数列表**
| 参数名称(不区分大小写) | 解释 |
| :-------------------------- | :----------------------------------------------------------- |
| path, prefixPath | 查询路径; 必填项 |
| T | 查询时间戳(只能指定一个); 必填项 |
| data_type | 填充方法使用的数据类型。 可选值是int32,int64,float,double,boolean,text; 可选字段 |
| before\_range, after\_range | 表示线性方法的有效时间范围。 当[T-before_range,T + after_range]范围内的值存在时,前一种方法将起作用。 如果未明确指定before_range和after_range,则使用default\_fill\_interval。 -1表示无穷大; 可选字段 |
</center>
需要注意的是一旦时间序列在查询时间戳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(float [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
在 [样例数据](https://github.com/thulab/iotdb/files/4438687/OtherMaterial-Sample.Data.txt), 该语句的执行结果如下所示:
```
+-----------------------------+-------------------------------+
| Time|root.sgcc.wf03.wt01.temperature|
+-----------------------------+-------------------------------+
|2017-11-01T16:37:50.000+08:00| 24.746666|
+-----------------------------+-------------------------------+
Total line number = 1
It costs 0.017s
```
数据类型和填充方法之间的对应关系
数据类型和支持的填充方法如表3-6所示。
<center>**表3-6数据类型和支持的填充方法**
| 数据类型 | 支持的填充方法 |
| :------- | :--------------- |
| boolean | previous |
| int32 | previous, linear |
| int64 | previous, linear |
| float | previous, linear |
| double | previous, linear |
| text | previous |
</center>
> 注意:应在Fill语句中至少指定一种填充方法。
* 时间区间分组聚合查询补空值
时间区间分组聚合出的各个时间段的结果,支持使用前值补空。
不允许设置滑动步长,默认为聚合时间区间,实际为定长采样。现在只支持 last_value 聚合函数。
目前不支持线性插值补空值。
PREVIOUS 和 PREVIOUSUNTILLAST 的区别
* PREVIOUS:只要空值前边有值,就会用其填充空值。
* PREVIOUSUNTILLAST:不会填充此序列最新点后的空值
首先我们检查一下root.ln.wf01.wt01.temperature在时间2017-11-07T23:50:00以后的值
```
IoTDB> SELECT temperature FROM root.ln.wf01.wt01 where time >= 2017-11-07T23:50:00
+-----------------------------+-----------------------------+
| Time|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| 20.18|
|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| 25.9|
|2017-11-07T23:56:00.000+08:00| 24.44|
|2017-11-07T23:57:00.000+08:00| 24.39|
|2017-11-07T23:58:00.000+08:00| 22.93|
|2017-11-07T23:59:00.000+08:00| 21.07|
+-----------------------------+-----------------------------+
Total line number = 10
It costs 0.002s
```
我们发现root.ln.wf01.wt01.temperature的最后时间和值是2017-11-07T23:59:00和21.07。
SQL 示例:
```
SELECT last_value(temperature) FROM root.ln.wf01.wt01 GROUP BY([2017-11-07T23:50:00, 2017-11-08T00:01:00),1m) FILL (float[PREVIOUSUNTILLAST]);
SELECT last_value(temperature) FROM root.ln.wf01.wt01 GROUP BY([2017-11-07T23:50:00, 2017-11-08T00:01:00),1m) FILL (float[PREVIOUS]);
```
结果:
```
+-----------------------------+-----------------------------------------+
| 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| 20.18|
|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| 25.9|
|2017-11-07T23:56:00.000+08:00| 24.44|
|2017-11-07T23:57:00.000+08:00| 24.39|
|2017-11-07T23:58:00.000+08:00| 22.93|
|2017-11-07T23:59:00.000+08:00| 21.07|
|2017-11-08T00:00:00.000+08:00| null|
+-----------------------------+-----------------------------------------+
Total line number = 11
It costs 0.005s
+-----------------------------+-----------------------------------------+
| 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| 20.18|
|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| 25.9|
|2017-11-07T23:56:00.000+08:00| 24.44|
|2017-11-07T23:57:00.000+08:00| 24.39|
|2017-11-07T23:58:00.000+08:00| 22.93|
|2017-11-07T23:59:00.000+08:00| 21.07|
|2017-11-08T00:00:00.000+08:00| 21.07|
+-----------------------------+-----------------------------------------+
Total line number = 11
It costs 0.006s
```
解释:
使用 PREVIOUSUNTILLAST 将不会填充2017-11-07T23:59:00以后的值。
#### LIMIT & OFFSET
IoTDB提供 [LIMIT/SLIMIT](../Appendix/SQL-Reference.md) 子句和 [OFFSET/SOFFSET](../Appendix/SQL-Reference.md) 子句,以使用户可以更好地控制查询结果。使用LIMIT和SLIMIT子句可让用户控制查询结果的行数和列数,
并且使用OFFSET和SOFSET子句允许用户设置结果显示的起始位置。
请注意,按组查询不支持LIMIT和OFFSET。
本章主要介绍查询结果的行和列控制的相关示例。你还可以使用 [Java JDBC](../API/Programming-JDBC.md) 标准接口执行查询。
* 查询结果的行控制
通过使用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(float[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, GROUP, DISABLE, ALIGN}
```
* 查询结果的列控制
通过使用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(float[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
```
#### 别名
由于 IoTDB 独特的数据模型,在每个传感器前都附带有设备等诸多额外信息。有时,我们只针对某个具体设备查询,而这些前缀信息频繁显示造成了冗余,影响了结果集的显示与分析。这时我们可以使用 IoTDB 提供的 AS 函数,将查询中出现的时间序列给定一个别名。
例如:
```
select s1 as temperature, s2 as speed from root.ln.wf01.wt01;
```
则结果集将显示为:
| Time | temperature | speed |
| ---- | ----------- | ----- |
| ... | ... | ... |
#### 内置函数
时间序列生成函数可接受若干原始时间序列作为输入,产生一列时间序列输出。与聚合函数不同的是,时间序列生成函数的结果集带有时间戳列。
所有的时间序列生成函数都可以接受 * 作为输入,都可以与原始查询混合进行。
* 数学函数
目前IoTDB支持下列数学函数,这些数学函数的行为与这些函数在Java Math标准库中对应实现的行为一致。
| 函数名 | 输入序列类型 | 输出序列类型 | Java标准库中的对应实现 |
| ------- | ------------------------------ | ------------------------ | ------------------------------------------------------------ |
| SIN | INT32 / INT64 / FLOAT / DOUBLE | DOUBLE | Math#sin(double) |
| COS | INT32 / INT64 / FLOAT / DOUBLE | DOUBLE | Math#cos(double) |
| TAN | INT32 / INT64 / FLOAT / DOUBLE | DOUBLE | Math#tan(double) |
| ASIN | INT32 / INT64 / FLOAT / DOUBLE | DOUBLE | Math#asin(double) |
| ACOS | INT32 / INT64 / FLOAT / DOUBLE | DOUBLE | Math#acos(double) |
| ATAN | INT32 / INT64 / FLOAT / DOUBLE | DOUBLE | Math#atan(double) |
| DEGREES | INT32 / INT64 / FLOAT / DOUBLE | DOUBLE | Math#toDegrees(double) |
| RADIANS | INT32 / INT64 / FLOAT / DOUBLE | DOUBLE | Math#toRadians(double) |
| ABS | INT32 / INT64 / FLOAT / DOUBLE | 与输入序列的实际类型一致 | Math#abs(int) / Math#abs(long) /Math#abs(float) /Math#abs(double) |
| SIGN | INT32 / INT64 / FLOAT / DOUBLE | DOUBLE | Math#signum(double) |
| CEIL | INT32 / INT64 / FLOAT / DOUBLE | DOUBLE | Math#ceil(double) |
| FLOOR | INT32 / INT64 / FLOAT / DOUBLE | DOUBLE | Math#floor(double) |
| ROUND | INT32 / INT64 / FLOAT / DOUBLE | DOUBLE | Math#rint(double) |
| EXP | INT32 / INT64 / FLOAT / DOUBLE | DOUBLE | Math#exp(double) |
| LN | INT32 / INT64 / FLOAT / DOUBLE | DOUBLE | Math#log(double) |
| LOG10 | INT32 / INT64 / FLOAT / DOUBLE | DOUBLE | Math#log10(double) |
| SQRT | INT32 / INT64 / FLOAT / DOUBLE | DOUBLE | Math#sqrt(double) |
例如:
``` sql
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_CONTAINS | TEXT | `s`: 待搜寻的字符串 | BOOLEAN | 判断字符串中是否存在`s` |
| STRING_MATCHES | TEXT | `regex`: Java标准库风格的正则表达式 | BOOLEAN | 判断字符串是否能够被正则表达式`regex`匹配 |
例如:
``` sql
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_K | INT32 / INT64 / FLOAT / DOUBLE / TEXT | `k`: 最多选择的数据点数,必须大于0小于等于1000 | 与输入序列的实际类型一致 | 返回某时间序列中值最大的`k`个数据点。若多于`k`个数据点的值并列最大,则返回时间戳最小的数据点。 |
| BOTTOM_K | INT32 / INT64 / FLOAT / DOUBLE / TEXT | `k`: 最多选择的数据点数,必须大于0小于等于1000 | 与输入序列的实际类型一致 | 返回某时间序列中值最小的`k`个数据点。若多于`k`个数据点的值并列最小,则返回时间戳最小的数据点。 |
例如:
``` sql
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_DIFFERENCE | INT32 / INT64 / FLOAT / DOUBLE / BOOLEAN / TEXT | INT64 | 统计序列中某数据点的时间戳与前一数据点时间戳的差。范围内第一个数据点没有对应的结果输出。 |
| DIFFERENCE | INT32 / INT64 / FLOAT / DOUBLE | 与输入序列的实际类型一致 | 统计序列中某数据点的值与前一数据点的值的差。范围内第一个数据点没有对应的结果输出。 |
| NON_NEGATIVE_DIFFERENCE | INT32 / INT64 / FLOAT / DOUBLE | 与输入序列的实际类型一致 | 统计序列中某数据点的值与前一数据点的值的差的绝对值。范围内第一个数据点没有对应的结果输出。 |
| DERIVATIVE | INT32 / INT64 / FLOAT / DOUBLE | DOUBLE | 统计序列中某数据点相对于前一数据点的变化率,数量上等同于 DIFFERENCE / TIME_DIFFERENCE。范围内第一个数据点没有对应的结果输出。 |
| NON_NEGATIVE_DERIVATIVE | INT32 / INT64 / FLOAT / DOUBLE | DOUBLE | 统计序列中某数据点相对于前一数据点的变化率的绝对值,数量上等同于 NON_NEGATIVE_DIFFERENCE / TIME_DIFFERENCE。范围内第一个数据点没有对应的结果输出。 |
例如:
``` sql
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
```
* 自定义序列生成函数
请参考 [UDF (用户定义函数)](../UDF/UDF-User-Defined-Function.md)。
#### 错误处理
当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>, SLIMIT, OFFSET, SOFFSET, GROUP, DISABLE, ALIGN}
```
当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
```
### 数据删除
用户使用[DELETE语句](../Appendix/SQL-Reference.md)可以删除指定的时间序列中符合时间删除条件的数据。在删除数据时,用户可以选择需要删除的一个或多个时间序列、时间序列的前缀、时间序列带\*路径对某一个时间区间内的数据进行删除。
在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之前的供电状态和设备硬件版本都需要删除,此时可以使用含义更广的[前缀路径或带`*`路径](../Data-Concept/Data-Model-and-Terminology.md)进行删除操作,进行此操作的SQL语句为:
```
delete from root.ln.wf02.wt02 where time <= 2017-11-01T16:26:00;
```
```
delete from root.ln.wf02.wt02.* where time <= 2017-11-01T16:26:00;
```
需要注意的是,当删除的路径不存在时,IoTDB会提示路径不存在,无法删除数据,如下所示。
```
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的配置文件中找到(如果您使用的版本支持时间分区)。
请注意该功能目前只是实验性的,如果您不是开发者,使用时请务必谨慎。