Time travel查詢與增量查詢
對于Delta Table類型的表,MaxCompute支持查詢回溯到源表某個歷史時間或者版本進行歷史Snapshot查詢(Time travel查詢),也支持指定源表某個歷史時間區(qū)間或者版本區(qū)間進行歷史增量查詢(Incremental查詢)。本文為您介紹Delta Table的查詢使用說明和使用限制。
命令格式
[with <cte>[, ...] ]
select [all | distinct] <select_expr>[, <except_expr>)][, <replace_expr>] ...
from <table_reference>
[timestamp | version as of expr]
[timestamp | version between start_expr and end_expr]
[where <where_condition>]
[group by {<col_list>|rollup(<col_list>)}]
[having <having_condition>]
[order by <order_condition>]
[distribute by <distribute_condition> [sort by <sort_condition>]|[ cluster by <cluster_condition>] ]
[limit <number>]
[window <window_clause>]
SQL DQL語法基本支持了查詢Delta Table的所有場景,并基本遵循MaxCompute DQL語法以及對應的約束。只增加了From Table 子句的語法增強,可以通過固定格式的表達式指定源表某個歷史時間或者版本進行歷史Snapshot查詢,也可以指定源表某個歷史時間區(qū)間或者版本區(qū)間進行歷史增量查詢。
Time travel查詢參數(shù)與使用限制
Time travel查詢Delta Table,回溯到源表某個歷史時間或者版本進行歷史Snapshot查詢。您可通過[timestamp | version as of expr]
來配置具體的查詢方式。
timestamp as of expr
參數(shù)說明
timestamp as of為固定語法格式,代表根據(jù)歷史時間查詢歷史snapshot數(shù)據(jù) 。
expr的取值類型可為MaxCompute標準的timestmap、datetime或date類型,目前可支持的形式有:
日期字符串常量:
timestmap | datetime | date類型的字符串常量,示例如下。
數(shù)據(jù)類型
示例
timestmap
'2023-01-01 00:00:00.123'
datetime
'2023-01-01 00:00:00'
date
'2023-01-01'
MaxCompute內(nèi)置時間函數(shù):
current_timestamp() | getDate() + N: current_timestamp() | getDate()
其中N的單位是秒,N為負數(shù),表示當前時間往前N秒,N為正數(shù),表示當前時間往后N秒。
Delta Table特殊語法:
get_latest_timestamp(string tablename [, bigint <number>])
如果是跨project訪問,tablename需寫為
projectName.tableName,
。如果是三層模型,tablename需寫為
projectName.schemaName.tableName;
。number可不填,默認值為1,表示時間從后往前序列中第number次數(shù)據(jù)操作的Commit時間,比如1表示最后一次操作,其中數(shù)據(jù)操作包括用戶側(cè)主動發(fā)送的數(shù)據(jù)修改操作和系統(tǒng)內(nèi)部發(fā)起的數(shù)據(jù)排布操作。不同的number參數(shù)返回的timestamp可能相同。
使用限制
查詢的歷史快照數(shù)據(jù)范圍為
[CreateTableTimestamp, expr]
,比較對象為每次DML操作生成的Commit時間,CreateTableTimestamp為表創(chuàng)建操作生成的Commit時間。expr返回的時間早于time travel時間周期(即創(chuàng)建Delta Table時配置的acid.data.retain.hours),或早于Delta Table表創(chuàng)建時間,會直接報錯,因為對應的歷史數(shù)據(jù)狀態(tài)可能不存在了,比如acid.data.retain.hours=72小時,exprs為72小時之前的時間,就會報錯。
expr返回的時間如果正好處于time travel時間周期(即創(chuàng)建Delta Table時配置的acid.data.retain.hours)的下限,由于內(nèi)部系統(tǒng)之間交互也有延時,所以有概率出現(xiàn)秒級的誤差,導致報錯,所以盡量不要使用類似于 (
timestamp as of current_timestamp() - time travel時間周期
) 的語法,容易觸發(fā)報錯。
version as of expr
參數(shù)說明
version as of 為固定語法格式,代表根據(jù)歷史數(shù)據(jù)操作的版本號(version)查詢歷史snapshot數(shù)據(jù)。
expr返回類型為MaxCompute的bigint整型,目前可支持的形式有:
整型常量:
比如常量
3
。Delta Table特殊語法:
get_latest_version(string tablename [, bigint <number>])
如果是跨project訪問,tablename需寫為
projectName.tableName,
。如果是三層模型,tablename需寫為
projectName.schemaName.tableName;
。number可不填,默認值為1,表示時間從后往前序列中第number次數(shù)據(jù)操作的版本號,比如1表示最后一次操作,其中數(shù)據(jù)操作包括用戶側(cè)主動發(fā)送的數(shù)據(jù)修改操作和系統(tǒng)內(nèi)部發(fā)起的數(shù)據(jù)排布操作。不同的number參數(shù)返回的version不相同。
使用限制
每次DML操作會產(chǎn)生嚴格遞增的version,您可通過
show history for table / partition
顯示所有DML操作信息,從中獲取對應操作的version。查詢的歷史快照數(shù)據(jù)范圍為
[CreateTableVersion, expr]
,比較對象為每次DML操作對應的version。 CreateTableVersion為表創(chuàng)建操作產(chǎn)生的version,默認為1。expr返回的version,系統(tǒng)會獲取它對應的DML Commit時間,如果早于配置的time travel時間周期(即創(chuàng)建Delta Table時配置的acid.data.retain.hours),或者version值小于1, 會直接報錯。
expr返回的version大于最近一次DML操作的version,直接報錯,推薦通過
get_latest_version
函數(shù)來獲取所需的版本號。
Incremental查詢參數(shù)與使用限制
Incremental查詢Delta Table,指定源表某個歷史時間區(qū)間或者版本區(qū)間進行歷史增量查詢。您可通過[timestamp | version between start_expr and end_expr]
來配置具體的查詢方式。
timestamp between start_expr and end_expr
參數(shù)說明
timestamp between and:為固定語法格式,代表根據(jù)歷史時間區(qū)間查詢歷史增量數(shù)據(jù)。
start_expr和end_expr用法以及約束同timestamp as of語法的expr保持一致。
使用限制
查詢的歷史增量數(shù)據(jù)范圍為
(start_expr,end_expr]
, 即左開右閉區(qū)間,比較對象為每次DML操作生成的Commit時間。start_expr早于配置的time travel時間周期(acid.data.retain.hours),或者早于表創(chuàng)建時間,會直接報錯。
end_expr晚于最近一次DML操作的Commit時間時,查詢行為結(jié)果根據(jù)表屬性(acid.incremental.query.out.of.time.range.enabled)的取值來決定:
設(shè)置為false(默認值)時,會直接報錯
設(shè)置為true時,會查詢包含
(start_expr,end_expr]
范圍內(nèi)的所有增量歷史數(shù)據(jù)。您可通過alter table修改此參數(shù)的取值,例如:
alter table mf_tt2 set tblproperties("acid.incremental.query.out.of.time.range.enabled"="true");
version between start_expr and end_expr
參數(shù)說明
version between and 為固定語法格式,代表根據(jù)歷史DML操作的version區(qū)間查詢歷史增量數(shù)據(jù)。
start_expr和end_expr用法以及約束同version as of 語法的expr保持一致,參考上面描述。
使用限制
查詢的歷史增量數(shù)據(jù)范圍是(start_expr,end_expr], 即左開右閉區(qū)間,比較對象為每次DML操作生成的version。
start_expr返回的version,系統(tǒng)會獲取它對應的DML Commit時間,如果早于配置的time travel時間周期(acid.data.retain.hours), 或者version值小于1,會直接報錯。
end_expr返回的version晚于最近一次DML操作的version的行為通過表屬性(acid.incremental.query.out.of.time.range.enabled)來決定,默認值為false,會直接報錯,如果設(shè)置為true,會查詢包含(start_expr,end_expr]范圍內(nèi)的所有增量歷史數(shù)據(jù)。
其他使用說明
相同Key的多行記錄,只返回最近的一行記錄,如果最后一行是Delete狀態(tài),直接過濾掉。 后續(xù)版本考慮支持類似CDC格式的數(shù)據(jù)更新狀態(tài)查詢。
不支持表對象不存在的表的歷史數(shù)據(jù)查詢,例如對表進行drop、rename等刪除操作。
此種情況下您可先通過restore操作恢復表對象,然后再繼續(xù)查詢。
目前只支持Delta Table的time travel/incremental查詢,其他表不支持。
同一個SQL中的同一張表,對于time travel和incremental查詢,timestamp或者version必須相同。
分區(qū)表查詢最好指定partition,避免查詢所有partition的歷史操作日志導致耗時不穩(wěn)定。
讀寫并發(fā)事務(wù)采用MVCC模型,可保障讀寫隔離,相互不影響,支持ReadCommited級別。
Compaction操作生成數(shù)據(jù)不認為是新增數(shù)據(jù),因此增量查詢出來的數(shù)據(jù)不會包含。
使用示例
示例數(shù)據(jù)
--創(chuàng)建表操作,產(chǎn)生的version為1,執(zhí)行show history for table mf_tt2, 可查詢version; create table mf_tt2 (pk bigint not null primary key, val bigint not null) partitioned by (dd string, hh string) tblproperties ("transactional"="true"); --DML操作,產(chǎn)生的version為2 insert overwrite table mf_tt2 partition(dd='01', hh='01') values (1, 1), (2, 2), (3, 3); --DML操作,產(chǎn)生的version為3 insert into table mf_tt2 partition(dd='01', hh='01') values (3, 30), (4, 4), (5, 5);
表相關(guān)數(shù)據(jù)查詢
查詢表創(chuàng)建時間,作為后續(xù)設(shè)置查詢歷史時間的參考
desc extended mf_tt2;
返回值:
+------------------------------------------------------------------------------------+ | Owner: ALIYUN$****_doctest@test.aliyunid.com | Project: doc_test_prod | | TableComment: | +------------------------------------------------------------------------------------+ | CreateTime: 2023-06-26 09:31:38 | | LastDDLTime: 2023-06-26 09:31:38 | | LastModifiedTime: 2023-06-26 09:32:31 | +------------------------------------------------------------------------------------+ | InternalTable: YES | Size: 8541 | +------------------------------------------------------------------------------------+ | Native Columns: | +------------------------------------------------------------------------------------+ | Field | Type | Label | ExtendedLabel | Nullable | DefaultValue | Comment | +------------------------------------------------------------------------------------+ | pk | bigint | | | false | NULL | | | val | bigint | | | false | NULL | | +------------------------------------------------------------------------------------+ | Partition Columns: | +------------------------------------------------------------------------------------+ | dd | string | | | hh | string | | +------------------------------------------------------------------------------------+ | Extended Info: | +------------------------------------------------------------------------------------+ | TableID: bec515a56cc9492c8f906a224c62**** | | IsArchived: false | | PhysicalSize: 25623 | | FileNum: 9 | | StoredAs: AliOrc | | CompressionStrategy: normal | | ClusterType: hash | | BucketNum: 16 | | ClusterColumns: [pk] | | SortColumns: [pk ASC] | +------------------------------------------------------------------------------------+
查詢歷史數(shù)據(jù)操作的版本號
show history for table mf_tt2 partition(dd='01',hh='01');
查詢結(jié)果
ID = 20230626021756157ghict5k**** ObjectType ObjectId ObjectName VERSION(LSN) Time Operation PARTITION 4764c8e1cb634a4fb9c21f3fc850**** dd=01/hh=01 0000000000000002 2023-06-26 09:31:56 CREATE PARTITION 4764c8e1cb634a4fb9c21f3fc850**** dd=01/hh=01 0000000000000003 2023-06-26 09:32:32 APPEND
Time travel查詢示例
查詢截止到指定時間(例如datetime格式的字符串常量)的所有歷史數(shù)據(jù)
select * from mf_tt2 timestamp as of '2023-06-26 09:33:00' where dd = '01' and hh = '01';
返回值為:
+------------+------------+----+----+ | pk | val | dd | hh | +------------+------------+----+----+ | 1 | 1 | 01 | 01 | | 3 | 30 | 01 | 01 | | 4 | 4 | 01 | 01 | | 5 | 5 | 01 | 01 | | 2 | 2 | 01 | 01 | +------------+------------+----+----+
查詢截止到指定version常量的所有歷史數(shù)據(jù)
select * from mf_tt2 version as of 2 where dd = '01' and hh = '01';
返回值為:
+------------+------------+----+----+ | pk | val | dd | hh | +------------+------------+----+----+ | 1 | 1 | 01 | 01 | | 3 | 3 | 01 | 01 | | 2 | 2 | 01 | 01 | +------------+------------+----+----+
查詢截止到當前時間的所有歷史數(shù)據(jù),即全量查詢
select * from mf_tt2 timestamp as of current_timestamp() where dd = '01' and hh = '01';
返回值為:
+------------+------------+----+----+ | pk | val | dd | hh | +------------+------------+----+----+ | 1 | 1 | 01 | 01 | | 3 | 30 | 01 | 01 | | 4 | 4 | 01 | 01 | | 5 | 5 | 01 | 01 | | 2 | 2 | 01 | 01 | +------------+------------+----+----+
查詢截止到10s前的所有歷史數(shù)據(jù)
select * from mf_tt2 timestamp as of current_timestamp() - 10 where dd = '01' and hh = '01';
返回值為:
+------------+------------+----+----+ | pk | val | dd | hh | +------------+------------+----+----+ | 1 | 1 | 01 | 01 | | 3 | 30 | 01 | 01 | | 4 | 4 | 01 | 01 | | 5 | 5 | 01 | 01 | | 2 | 2 | 01 | 01 | +------------+------------+----+----+
查詢截止到最近第二次commit的所有歷史數(shù)據(jù)
示例1
select * from mf_tt2 timestamp as of get_latest_timestamp('mf_tt2', 2) where dd = '01' and hh = '01';
示例2
select * from mf_tt2 version as of get_latest_version('mf_tt2', 2) where dd = '01' and hh = '01';
返回值為
+------------+------------+----+----+ | pk | val | dd | hh | +------------+------------+----+----+ | 1 | 1 | 01 | 01 | | 3 | 3 | 01 | 01 | | 2 | 2 | 01 | 01 | +------------+------------+----+----+
Incremental查詢示例
查詢指定時間(例如datetime格式的字符串常量)區(qū)間的歷史增量數(shù)據(jù),常量值需要根據(jù)具體操作的時間來配置
select * from mf_tt2 timestamp between '2023-06-26 09:31:40' and '2023-06-26 09:32:00' where dd = '01' and hh = '01';
返回值為:
+------------+------------+----+----+ | pk | val | dd | hh | +------------+------------+----+----+ | 1 | 1 | 01 | 01 | | 3 | 3 | 01 | 01 | | 2 | 2 | 01 | 01 | +------------+------------+----+----+
查詢指定version區(qū)間的歷史增量數(shù)據(jù)
select * from mf_tt2 version between 2 and 3 where dd = '01' and hh = '01';
返回值為:
+------------+------------+----+----+ | pk | val | dd | hh | +------------+------------+----+----+ | 3 | 30 | 01 | 01 | | 4 | 4 | 01 | 01 | | 5 | 5 | 01 | 01 | +------------+------------+----+----+
查詢最近300s內(nèi)的歷史增量數(shù)據(jù)
示例:表acid.incremental.query.out.of.time.range.enabled屬性為默認值false
select * from mf_tt2 timestamp between current_timestamp() - 301 and current_timestamp() where dd = '01' and hh='01';
返回值報錯:
FAILED: ODPS-0130071:[0,0] Semantic analysis exception - physical plan generation failed: com.aliyun.odps.meta.exception.MetaException: com.aliyun.odps.meta.exception.MetaException: com.aliyun.odps.metadata.common.MetastoreServerException: Incremental query can't exceed current version. Current version timestamp: 2023-06-26 09:32:32, input timestamp is: 2023-06-26 10:47:55
示例:修改表acid.incremental.query.out.of.time.range.enabled屬性為true
alter table mf_tt2 set tblproperties("acid.incremental.query.out.of.time.range.enabled"="true");
示例:再次查詢
select * from mf_tt2 timestamp between current_timestamp() - 301 and current_timestamp() where dd = '01' and hh='01';
返回值為:
+------------+------------+----+----+ | pk | val | dd | hh | +------------+------------+----+----+ +------------+------------+----+----+
查詢最近兩次commit的歷史增量數(shù)據(jù)
示例
select * from mf_tt2 timestamp between get_latest_timestamp('mf_tt2', 3) and get_latest_timestamp('mf_tt2') where dd = '01' and hh = '01';
示例
select * from mf_tt2 version between get_latest_version('mf_tt2', 3) and get_latest_version('mf_tt2') where dd = '01' and hh = '01';
返回值
+------------+------------+----+----+ | pk | val | dd | hh | +------------+------------+----+----+ | 1 | 1 | 01 | 01 | | 3 | 30 | 01 | 01 | | 4 | 4 | 01 | 01 | | 5 | 5 | 01 | 01 | | 2 | 2 | 01 | 01 | +------------+------------+----+----+