时序数据中的 NULL 值

NULL 值是空白填充和插值 (GFI) 计算的不常见输入。当存在 NULL 值时,您可以使用时间序列聚合 (TSA) 函数 TS_FIRST_VALUETS_LAST_VALUEIGNORE NULLS 影响插值的输出。系统处理 TSA 函数的方式与处理其对应的分析函数 FIRST_VALUELAST_VALUE 类似,因此,如果时间戳本身为 NULL,Vertica 将先筛选掉这些行,然后再执行空白填充和插值。

包含 NULL 值时的常数插值

图 1 显示了四个输入行中的输入都不包含 NULL 值时的默认(常数)插值结果。

图 2 显示了相同的输入行,但新增的一个输入记录的出价值为 NULL 且其对应的时间戳值为 3:00:03。

对于常数插值,从 3:00:03 开始的出价值将一直为 NULL,直到时序中出现下一个非 NULL 出价值。在图 2 中,由于存在 NULL 行,以阴影区域表示的时间间隔中的内插出价值为 NULL。如果在以 3:00:02 为起点的时间片中,使用常数插值对 TS_FIRST_VALUE(bid) 进行评估,其输出为非 NULL。但是,TS_FIRST_VALUE(bid) 在下一个时间片中仍会输出 NULL。如果 3:00:02 时间片的最后一个值为 NULL,则下一个时间片 (3:00:04) 的第一个值为 NULL。但是,如果将 TSA 函数与 IGNORE NULLS 结合使用,则在 3:00:04 时的值将与在 3:00:02 时的值相同。

为说明这一情况,请在 TickStore 表的 03:00:03 处插入一个包含 NULL 出价值的新行,此时 Vertica 将为 03:00:02 记录输出一个包含 NULL 值的行,但是没有为 03:00:03 输入输出一个行。

=> INSERT INTO tickstore VALUES('2009-01-01 03:00:03', 'XYZ', NULL);
=> SELECT slice_time, symbol, TS_LAST_VALUE(bid) AS last_bid FROM TickStore
-> TIMESERIES slice_time AS '2 seconds' OVER (PARTITION BY symbol ORDER BY ts);
     slice_time      | symbol | last_bid
---------------------+--------+----------
 2009-01-01 03:00:00 | XYZ    |       10
 2009-01-01 03:00:02 | XYZ    |
 2009-01-01 03:00:04 | XYZ    |     10.5
(3 rows)

如果指定 IGNORE NULLS,Vertica 将使用常数插值方案填充缺失的数据点。在此,03:00:02 时的出价会内插到出价的上一个已知输入记录,即 03:00:00 时的 $10:

=> SELECT slice_time, symbol, TS_LAST_VALUE(bid IGNORE NULLS) AS last_bid FROM TickStore
     TIMESERIES slice_time AS '2 seconds' OVER (PARTITION BY symbol ORDER BY ts);
     slice_time      | symbol | last_bid
---------------------+--------+----------
 2009-01-01 03:00:00 | XYZ    |       10
 2009-01-01 03:00:02 | XYZ    |       10
 2009-01-01 03:00:04 | XYZ    |     10.5
(3 rows)

此时,假如插入一个其时间戳列包含 NULL 值的行,Vertica 将在空白填充和插值开始之前筛选掉此行。

=> INSERT INTO tickstore VALUES(NULL, 'XYZ', 11.2);
=> SELECT slice_time, symbol, TS_LAST_VALUE(bid) AS last_bid FROM TickStore
     TIMESERIES slice_time AS '2 seconds' OVER (PARTITION BY symbol ORDER BY ts);

请注意,11.2 出价行没有对应的输出。

     slice_time      | symbol | last_bid
---------------------+--------+----------
 2009-01-01 03:00:00 | XYZ    |       10
 2009-01-01 03:00:02 | XYZ    |
 2009-01-01 03:00:04 | XYZ    |     10.5
(3 rows)

包含 NULL 值时的线性插值

对于线性插值,在图 3 中以阴影区表示的时间间隔中,内插出价值为 NULL。

如果在 3:00:03 时的输入值为 NULL,Vertica 无法以线性方式在此时间点附近内插出价值。

Vertica 在时间片的任意一侧中选择最近的非 NULL 值,并使用此值。例如,如果您使用线性插值方案,但未指定 IGNORE NULLS,并且您的数据包含一个实值和一个 NULL 值,则结果为 NULL。如果任意一侧中有值为 NULL,则结果为 NULL。因此,如果在以 3:00:02 为起始的时间片中,使用线性插值对 TS_FIRST_VALUE(bid) 进行评估,其输出为 NULL。 TS_FIRST_VALUE(bid) 在下一个时间片中仍为 NULL。

=> SELECT slice_time, symbol, TS_FIRST_VALUE(bid, 'linear') AS fv_l FROM TickStore
     TIMESERIES slice_time AS '2 seconds' OVER (PARTITION BY symbol ORDER BY ts);
     slice_time      | symbol | fv_l
---------------------+--------+------
 2009-01-01 03:00:00 | XYZ    |   10
 2009-01-01 03:00:02 | XYZ    |
 2009-01-01 03:00:04 | XYZ    |
(3 rows)