查询补空值

当对序列进行分段聚合时,某一段时间可能不存在数据,则此段数据的聚合结果为空,但这种空值不利于进行数据可视化展示和分析,需要对空值进行填补。

查询补空值允许用户按照特定的方法对查询结果填充空值,如取前一个不为空的值,或线性插值。补空值之后的查询结果能更好地反映数据分布,有利于用户进行数据分析。

在 IoTDB 中,用户可以使用 FILL 子句指定某一时间点或一时间窗口数据缺失的情况下的填充模式。如果查询点空,则填充功能将不起作用。

填充方法

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

数据类型支持的填充方法
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 填充

当查询的时间戳下数据为空时,将使用前一个时间戳的值来填充空白。 语法定义如下:

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

下表给出了所有参数的详细说明:

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

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

  1. 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] 范围)进行填充和显示。

样例数据空值填充 - 图1 (opens new window) 中, 该语句的执行结果如下所示:

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

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

  1. IoTDB> select temperature from root.sgcc.wf03.wt01 where time = 2017-11-01T16:37:50.000 fill(previous, 1s)
  2. +-----------------------------+-------------------------------+
  3. | Time|root.sgcc.wf03.wt01.temperature|
  4. +-----------------------------+-------------------------------+
  5. |2017-11-01T16:37:50.000+08:00| null|
  6. +-----------------------------+-------------------------------+
  7. Total line number = 1
  8. It costs 0.004s

Linear 填充

当查询的时间戳下数据为空时,将使用前一个和下一个时间戳的值来填充空白。 语法定义如下:

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

下表给出了所有参数的详细说明:

参数名称(不区分大小写)解释
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 语句如下:

  1. 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

样例数据空值填充 - 图2 (opens new window) 中, 该语句的执行结果如下所示:

  1. +-----------------------------+-------------------------------+
  2. | Time|root.sgcc.wf03.wt01.temperature|
  3. +-----------------------------+-------------------------------+
  4. |2017-11-01T16:37:50.000+08:00| 24.747707|
  5. +-----------------------------+-------------------------------+
  6. Total line number = 1
  7. It costs 0.017s

Value 填充

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

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

下表给出了所有参数的详细说明:

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

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

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

  1. 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 进行填充:

样例数据空值填充 - 图3 (opens new window) 中, 该语句的执行结果如下所示:

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

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

示例:

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

结果:

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

降采样补空值

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

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

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

PREVIOUS 和 PREVIOUSUNTILLAST 的区别

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

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

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

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

SQL 示例:

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

结果:

  1. 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);
  2. +-----------------------------+-----------------------------------------+
  3. | Time|last_value(root.ln.wf01.wt01.temperature)|
  4. +-----------------------------+-----------------------------------------+
  5. |2017-11-07T23:50:00.000+08:00| null|
  6. |2017-11-07T23:51:00.000+08:00| 22.24|
  7. |2017-11-07T23:52:00.000+08:00| 22.24|
  8. |2017-11-07T23:53:00.000+08:00| 24.58|
  9. |2017-11-07T23:54:00.000+08:00| 22.52|
  10. |2017-11-07T23:55:00.000+08:00| 22.52|
  11. |2017-11-07T23:56:00.000+08:00| 22.52|
  12. |2017-11-07T23:57:00.000+08:00| 24.39|
  13. |2017-11-07T23:58:00.000+08:00| null|
  14. +-----------------------------+-----------------------------------------+
  15. Total line number = 9
  16. It costs 0.007s
  17. 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);
  18. +-----------------------------+-----------------------------------------+
  19. | Time|last_value(root.ln.wf01.wt01.temperature)|
  20. +-----------------------------+-----------------------------------------+
  21. |2017-11-07T23:50:00.000+08:00| null|
  22. |2017-11-07T23:51:00.000+08:00| 22.24|
  23. |2017-11-07T23:52:00.000+08:00| 22.24|
  24. |2017-11-07T23:53:00.000+08:00| 24.58|
  25. |2017-11-07T23:54:00.000+08:00| 22.52|
  26. |2017-11-07T23:55:00.000+08:00| 22.52|
  27. |2017-11-07T23:56:00.000+08:00| 22.52|
  28. |2017-11-07T23:57:00.000+08:00| 24.39|
  29. |2017-11-07T23:58:00.000+08:00| 24.39|
  30. +-----------------------------+-----------------------------------------+
  31. Total line number = 9
  32. 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 来填充第一个区间的数据,示例如下:

  1. 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);

结果:

  1. 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);
  2. +-----------------------------+-----------------------------------------+
  3. | Time|last_value(root.ln.wf01.wt01.temperature)|
  4. +-----------------------------+-----------------------------------------+
  5. |2017-11-07T23:50:00.000+08:00| 23.7|
  6. |2017-11-07T23:51:00.000+08:00| 22.24|
  7. |2017-11-07T23:52:00.000+08:00| 22.24|
  8. |2017-11-07T23:53:00.000+08:00| 24.58|
  9. |2017-11-07T23:54:00.000+08:00| 22.52|
  10. |2017-11-07T23:55:00.000+08:00| 22.52|
  11. |2017-11-07T23:56:00.000+08:00| null|
  12. |2017-11-07T23:57:00.000+08:00| 24.39|
  13. |2017-11-07T23:58:00.000+08:00| 24.39|
  14. +-----------------------------+-----------------------------------------+
  15. Total line number = 9
  16. 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 方式进行填充,示例如下:

  1. 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);

结果:

  1. 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);
  2. +-----------------------------+-----------------------------------------+
  3. | Time|last_value(root.ln.wf01.wt01.temperature)|
  4. +-----------------------------+-----------------------------------------+
  5. |2017-11-07T23:50:00.000+08:00| 22.970001|
  6. |2017-11-07T23:51:00.000+08:00| 22.24|
  7. |2017-11-07T23:52:00.000+08:00| 23.41|
  8. |2017-11-07T23:53:00.000+08:00| 24.58|
  9. |2017-11-07T23:54:00.000+08:00| 22.52|
  10. |2017-11-07T23:55:00.000+08:00| 23.143333|
  11. |2017-11-07T23:56:00.000+08:00| 23.766666|
  12. |2017-11-07T23:57:00.000+08:00| 24.39|
  13. |2017-11-07T23:58:00.000+08:00| 23.283333|
  14. +-----------------------------+-----------------------------------------+
  15. Total line number = 9
  16. It costs 0.008s

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

Value 填充

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

  1. 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)
  2. SELECT last_value(temperature) FROM root.ln.wf01.wt01 GROUP BY([2017-11-07T23:50:00, 2017-11-07T23:59:00),1m) FILL ('temperature')

结果:

  1. 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);
  2. +-----------------------------+-----------------------------------------+
  3. | Time|last_value(root.ln.wf01.wt01.temperature)|
  4. +-----------------------------+-----------------------------------------+
  5. |2017-11-07T23:50:00.000+08:00| 20.0|
  6. |2017-11-07T23:51:00.000+08:00| 22.24|
  7. |2017-11-07T23:52:00.000+08:00| 20.0|
  8. |2017-11-07T23:53:00.000+08:00| 24.58|
  9. |2017-11-07T23:54:00.000+08:00| 22.52|
  10. |2017-11-07T23:55:00.000+08:00| 20.0|
  11. |2017-11-07T23:56:00.000+08:00| 20.0|
  12. |2017-11-07T23:57:00.000+08:00| 24.39|
  13. |2017-11-07T23:58:00.000+08:00| 20.0|
  14. +-----------------------------+-----------------------------------------+
  15. Total line number = 9
  16. It costs 0.007s
  17. 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');
  18. +-----------------------------+-----------------------------------------+
  19. | Time|last_value(root.ln.wf01.wt01.temperature)|
  20. +-----------------------------+-----------------------------------------+
  21. |2017-11-07T23:50:00.000+08:00| null|
  22. |2017-11-07T23:51:00.000+08:00| 22.24|
  23. |2017-11-07T23:52:00.000+08:00| null|
  24. |2017-11-07T23:53:00.000+08:00| 24.58|
  25. |2017-11-07T23:54:00.000+08:00| 22.52|
  26. |2017-11-07T23:55:00.000+08:00| null|
  27. |2017-11-07T23:56:00.000+08:00| null|
  28. |2017-11-07T23:57:00.000+08:00| 24.39|
  29. |2017-11-07T23:58:00.000+08:00| null|
  30. +-----------------------------+-----------------------------------------+
  31. Total line number = 9
  32. It costs 0.005s