GFI 示例
此主题介绍了可以使用常数和线性插值方案编写的部分查询。
常数插值
第一个查询使用 TS_FIRST_VALUE() 和 TIMESERIES 子句将输入记录放入时长为 3 秒的时间片中,然后返回每个符号/时间片组合的第一个出价值(时间片开始时的值)。
注意
TIMESERIES 子句要求对时间戳列执行 ORDER BY 操作。=> SELECT slice_time, symbol, TS_FIRST_VALUE(bid) AS first_bid FROM TickStore
TIMESERIES slice_time AS '3 seconds' OVER (PARTITION BY symbol ORDER BY ts);
因为股票 XYZ
的出价在 3:00:03 时为 10.0,即第二个时间片的 first_bid
值。此值从 3:00:03 开始时一直为 10.0,因为 10.5 的输入值直到 3:00:05 时才产生。在这种情况下,根据在股票 XYZ
中见到的最后一个值为时间 3:00:03 推断了内插值:
slice_time | symbol | first_bid
---------------------+--------+-----------
2009-01-01 03:00:00 | XYZ | 10
2009-01-01 03:00:03 | XYZ | 10
(2 rows)
下一个示例将输入记录放入 2 秒时间片以返回每个股票代码/时间片组合的第一个出价值:
=> SELECT slice_time, symbol, TS_FIRST_VALUE(bid) AS first_bid FROM TickStore
TIMESERIES slice_time AS '2 seconds' OVER (PARTITION BY symbol ORDER BY ts);
此时结果包含 2 秒时间片中的三个记录,所有这些出现在 03:00:00 时的第一个输入行和在 3:00:05 时的第二个输入行之间。请注意,第二个和第三个输出记录与不存在输入记录的时间片对应。
slice_time | symbol | first_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
(3 rows)
使用同一个表架构时,下一个查询将 TS_LAST_VALUE() 与 TIMESERIES 结合使用以返回每个时间片最后的值(时间片结束时的值)。
注意
时序聚合函数会处理每个时间片中的数据。每个时间片会产生一行输出;如果存在分区表达式,则每个时间片的每个分区会产生一行输出。=> 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);
请注意,最后一个值输出行为 10.5,因为在时间 3:00:05 时的值 10.5 是从 3:00:04 开始的 2 秒时间片中的最后一个点。
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)
请记住,因为常数插值为默认方案,如果按下述方式使用 CONST 参数编写查询,则会返回相同的结果:
=> SELECT slice_time, symbol, TS_LAST_VALUE(bid, 'CONST') AS last_bid FROM TickStore
TIMESERIES slice_time AS '2 seconds' OVER (PARTITION BY symbol ORDER BY ts);
线性插值
根据常数插值示例(指定了 2 秒时间片)中所列的上述输入记录,通过线性插值得出的 TS_LAST_VALUE 结果如下:
=> SELECT slice_time, symbol, TS_LAST_VALUE(bid, 'linear') AS last_bid FROM TickStore
TIMESERIES slice_time AS '2 seconds' OVER (PARTITION BY symbol ORDER BY ts);
在结果中,没有返回最后一行的 last_bid 值,因为查询指定了 TS_LAST_VALUE,并且在 3:00:04 时间片之后没有要插值的任何数据点。
slice_time | symbol | last_bid
---------------------+--------+----------
2009-01-01 03:00:00 | XYZ | 10.2
2009-01-01 03:00:02 | XYZ | 10.4
2009-01-01 03:00:04 | XYZ |
(3 rows)
使用多个时序聚合函数
同一个查询中可以使用多个时序聚合函数。按照 TIMESERIES 子句中的定义,它们共享同一个空白填充策略;然而,每个时序聚合函数可以指定自己的插值策略。在以下示例中,有两个常数插值和一个线性插值方案,但这三个函数全部使用三秒时间片。
=> SELECT slice_time, symbol,
TS_FIRST_VALUE(bid, 'const') fv_c,
TS_FIRST_VALUE(bid, 'linear') fv_l,
TS_LAST_VALUE(bid, 'const') lv_c
FROM TickStore
TIMESERIES slice_time AS '3 seconds' OVER(PARTITION BY symbol ORDER BY ts);
在以下输出中,比较了原始输出与多个时序聚合函数返回的输出。
使用分析 LAST_VALUE 函数
以下是使用 LAST_VALUE() 的示例,这样您可以了解此函数与 GFI 语法之间的区别。
=> SELECT *, LAST_VALUE(bid) OVER(PARTITION by symbol ORDER BY ts)
AS "last bid" FROM TickStore;
对于输出值,没有执行任何空白填充和内插。
ts | symbol | bid | last bid
---------------------+--------+------+----------
2009-01-01 03:00:00 | XYZ | 10 | 10
2009-01-01 03:00:05 | XYZ | 10.5 | 10.5
(2 rows)
使用 slice_time
在 TIMESERIES 查询中,您无法在 WHERE 子句中使用 slice_time
列,因为 WHERE 子句的评估先于 TIMESERIES 子句,并且直到评估 TIMESERIES 之后才会生成 slice_time
列。例如,Vertica 不支持以下查询:
=> SELECT symbol, slice_time, TS_FIRST_VALUE(bid IGNORE NULLS) AS fv
FROM TickStore
WHERE slice_time = '2009-01-01 03:00:00'
TIMESERIES slice_time as '2 seconds' OVER (PARTITION BY symbol ORDER BY ts);
ERROR: Time Series timestamp alias/Time Series Aggregate Functions not allowed in WHERE clause
但是,您可以编写子查询,将 slice_time
中的谓词放在外部查询中:
=> SELECT * FROM (
SELECT symbol, slice_time,
TS_FIRST_VALUE(bid IGNORE NULLS) AS fv
FROM TickStore
TIMESERIES slice_time AS '2 seconds'
OVER (PARTITION BY symbol ORDER BY ts) ) sq
WHERE slice_time = '2009-01-01 03:00:00';
symbol | slice_time | fv
--------+---------------------+----
XYZ | 2009-01-01 03:00:00 | 10
(1 row)
创建稠密时间序列
借助 TIMESERIES 子句,您可以方便地创建稠密时间序列,以便与实际数据一起用于外部联接。结果将表示所有时间点,而不只是存在数据的时间点。
随后的示例将使用空白填充和插值 (GFI)中所述的相同 TickStore 架构,并添加一个新内部表以用于创建联接:
=> CREATE TABLE inner_table (
ts TIMESTAMP,
bid FLOAT
);
=> CREATE PROJECTION inner_p (ts, bid) as SELECT * FROM inner_table
ORDER BY ts, bid UNSEGMENTED ALL NODES;
=> INSERT INTO inner_table VALUES ('2009-01-01 03:00:02', 1);
=> INSERT INTO inner_table VALUES ('2009-01-01 03:00:04', 2);
=> COMMIT;
为了返回所有时间点,可以在相关时间帧的开始和结束范围之间创建一个简单的并集。在本例中以 1 秒为时间段:
=> SELECT ts FROM (
SELECT '2009-01-01 03:00:00'::TIMESTAMP AS time FROM TickStore
UNION
SELECT '2009-01-01 03:00:05'::TIMESTAMP FROM TickStore) t
TIMESERIES ts AS '1 seconds' OVER(ORDER BY time);
ts
---------------------
2009-01-01 03:00:00
2009-01-01 03:00:01
2009-01-01 03:00:02
2009-01-01 03:00:03
2009-01-01 03:00:04
2009-01-01 03:00:05
(6 rows)
下一个查询将以 500 毫秒为时间段在时间帧的开始和结束范围之间创建一个并集:
=> SELECT ts FROM (
SELECT '2009-01-01 03:00:00'::TIMESTAMP AS time
FROM TickStore
UNION
SELECT '2009-01-01 03:00:05'::TIMESTAMP FROM TickStore) t
TIMESERIES ts AS '500 milliseconds' OVER(ORDER BY time);
ts
-----------------------
2009-01-01 03:00:00
2009-01-01 03:00:00.5
2009-01-01 03:00:01
2009-01-01 03:00:01.5
2009-01-01 03:00:02
2009-01-01 03:00:02.5
2009-01-01 03:00:03
2009-01-01 03:00:03.5
2009-01-01 03:00:04
2009-01-01 03:00:04.5
2009-01-01 03:00:05
(11 rows)
以下查询以 1 秒为时间段在相关时间帧的开始和结束范围之间创建一个并集:
=> SELECT * FROM (
SELECT ts FROM (
SELECT '2009-01-01 03:00:00'::timestamp AS time FROM TickStore
UNION
SELECT '2009-01-01 03:00:05'::timestamp FROM TickStore) t
TIMESERIES ts AS '1 seconds' OVER(ORDER BY time) ) AS outer_table
LEFT OUTER JOIN inner_table ON outer_table.ts = inner_table.ts;
此并集将从左联接表返回一组与右联接表中记录相匹配的完整记录。如果查询没有找到任何匹配项,它会使用 NULL 值扩展右侧列:
ts | ts | bid
---------------------+---------------------+-----
2009-01-01 03:00:00 | |
2009-01-01 03:00:01 | |
2009-01-01 03:00:02 | 2009-01-01 03:00:02 | 1
2009-01-01 03:00:03 | |
2009-01-01 03:00:04 | 2009-01-01 03:00:04 | 2
2009-01-01 03:00:05 | |
(6 rows)