- 类别:
AT | BEFORE¶
AT 或 BEFORE 子句用于 Snowflake Time Travel。在查询中,它在 FROM 子句中紧接在表名之后指定,并确定请求对象历史数据的过去时间点:
AT 关键字指定请求包含时间戳等于指定参数的语句或事务所做的任何更改。
BEFORE 关键字指定请求引用紧挨着指定参数之前的点。如果指定的参数是语句的查询 ID,则此点正好在语句完成之前。有关更多信息,请参阅 使用带有查询 ID 的 BEFORE 子句。
有关更多信息,请参阅 了解和使用 Time Travel。
- 另请参阅:
语法¶
SELECT ...
FROM ...
{
AT( { TIMESTAMP => <timestamp> | OFFSET => <time_difference> | STATEMENT => <id> | STREAM => '<name>' } ) |
BEFORE( STATEMENT => <id> )
}
[ ... ]
TIMESTAMP => timestamp
指定要用于 Time Travel 的确切日期和时间。该值必须显式转换为 TIMESTAMP。
OFFSET => time_difference
指定自当前时间开始的、要用于 Time Travel 的时间差值(以秒为单位),其格式为
-N
,其中N
可以是整数或算术表达式(例如,-120
为 120 秒、-30*60
为 1800 秒或 30 分钟)。STATEMENT => id
指定要用作 Time Travel 参考点的语句的查询 ID。此参数支持以下某一类型的任意语句:
DML(例如 INSERT、UPDATE、DELETE)
TCL(BEGIN、COMMIT 事务)
SELECT
查询 ID 必须引用过去 14 天内执行的查询。如果查询 ID 引用的查询超过 14 天,则返回以下错误:
Error: statement <query_id> not found
若要解决此限制,请使用引用查询的时间戳。
STREAM => 'name'
指定已查询的表或视图上现有流的标识符(即名称)。流中的当前偏移用作返回源对象的变更数据的
AT
时间点。只有在创建流(使用 CREATE STREAM)或查询更改数据(使用 CHANGES 子句)时才支持此关键字。有关示例,请参阅以下主题。
使用带有查询 ID 的 BEFORE 子句¶
如果 BEFORE 子句的参数是查询 ID,则 Time Travel 使用的过去点正好在语句完成之前,而不是在语句开始之前。如果并发查询在语句开始和结束之间提交了对数据的修改,则这些更改会包含在结果中。
例如,以下语句在两个独立的线程中在 my_table
表上同时执行:
时间 |
线程 |
操作 |
阶段 |
描述 |
---|---|---|---|---|
|
1 |
INSERT INTO my_table(id) VALUE(1) |
开始时间 |
插入通过执行必要的检查开始执行。 |
|
1 |
INSERT INTO my_table(id) VALUE(1) |
结束时间 |
插入更新后的 |
|
1 |
DELETE FROM my_table |
开始时间 |
删除操作标识要删除的记录列表 (id=1)。 |
|
2 |
INSERT INTO my_table(id) VALUE(2) |
开始时间 |
插入通过执行必要的检查开始执行。 |
|
2 |
INSERT INTO my_table(id) VALUE(2) |
结束时间 |
插入更新后的 |
|
2 |
SELECT * FROM my_table |
结束时间 |
线程 |
|
1 |
DELETE FROM my_table |
结束时间 |
当删除语句在线程 |
|
1 |
SELECT * FROM my_table BEFORE(STATEMENT => LAST_QUERY_ID()) |
结束时间 |
SELECT 语句使用 Time Travel 来检索删除操作完成前的历史数据。结果包含来自第 2 条插入语句的行,该语句在线程 |
作为解决方法,可以使用 TIMESTAMP 参数指定正好在语句开始之前的时间点。
使用说明¶
Snowflake 中的数据由时间戳标识,时间戳可能与系统时间的确切值略有不同。
TIMESTAMP 或 OFFSET 的值必须是常量表达式。
TIMESTAMP 的最小时间分辨率为毫秒。
如果请求的数据超出了 Time Travel 保留期(默认值为 1 天),则语句将失败。
此外,如果请求的数据在 Time Travel 保留期内,但没有可用的历史数据(例如保留期已延长的情况),则语句将失败。
如果指定的 Time Travel 时间在创建对象的时间点或之前,则语句将失败。
访问历史表数据时,结果包含表当前定义中的列和默认值等。非物化视图也是如此。例如,如果更改表以添加列,则查询在添加列的时间点之前的历史数据会返回包含新列的结果。
历史数据与当前数据具有相同的访问控制要求。任何更改都具有追溯性。
AT 和 BEFORE 子句不支持通过 CTE 选择历史数据。
例如,不支持以下查询:
WITH mycte AS (SELECT mytable.* FROM mytable) SELECT * FROM mycte AT(TIMESTAMP => '2024-03-13 13:56:09.553 +0100'::TIMESTAMP_TZ);
不过,在 WITH 子句的查询中支持这些子句。例如,支持以下查询:
WITH mycte AS (SELECT * FROM mytable AT(TIMESTAMP => '2024-03-13 13:56:09.553 +0100'::TIMESTAMP_TZ)) SELECT * FROM mycte;
故障排除¶
错误 |
Time travel data is not available for table <tablename>
|
---|---|
原因 |
在某些情况下,这是由于使用需要时间戳的字符串引起的。 |
解决方案 |
将字符串转换为时间戳。 ... AT(TIMESTAMP => '2018-07-27 12:00:00') -- fails
... AT(TIMESTAMP => '2018-07-27 12:00:00'::TIMESTAMP) -- succeeds
|
示例¶
使用特定时间戳从表中选择历史数据:
SELECT * FROM my_table AT(TIMESTAMP => 'Fri, 01 May 2015 16:20:00 -0700'::timestamp);
SELECT * FROM my_table AT(TIMESTAMP => TO_TIMESTAMP(1432669154242, 3));
从表中选择截至 5 分钟前的历史数据:
SELECT * FROM my_table AT(OFFSET => -60*5) AS T WHERE T.flag = 'valid';
从表中选择历史数据,但不包括指定事务所做的任何更改:
SELECT * FROM my_table BEFORE(STATEMENT => '8e5d0ca9-005e-44e6-b858-a8f5b37c5726');
返回指定事务导致的表数据差异:
SELECT oldt.* ,newt.*
FROM my_table BEFORE(STATEMENT => '8e5d0ca9-005e-44e6-b858-a8f5b37c5726') AS oldt
FULL OUTER JOIN my_table AT(STATEMENT => '8e5d0ca9-005e-44e6-b858-a8f5b37c5726') AS newt
ON oldt.id = newt.id
WHERE oldt.id IS NULL OR newt.id IS NULL;