はじめに
Apache Icebergは、データがコミットされた時点の状態をスナップショットとして管理しています。そのため、データを更新した後に、「更新前の集計結果をもう一度確認したい」という場面で遡って過去の状態を参照できます。
Autonomous AI Database(ADB)では、Iceberg外部表にOracle SQLのAS OF TIMESTAMPを付けて、指定した時刻の過去スナップショットを参照できます。
使うのは、通常のSELECT文にAS OF TIMESTAMPを追加したSQLです。
SELECT *
FROM iceberg_orders_demo
AS OF TIMESTAMP <参照したい時刻>;
このSQLを実行すると、ADBは指定時刻以下のIcebergスナップショットから最新のものを選び、Object Storage上のデータを読み出します。
今回は、このApache IcebergのタイムトラベルをADBのDatabase Actionを使って行なってみます。
基本仕様と前提
公式ドキュメントでは、ADBからApache Iceberg外部表の過去の状態を参照する方法として、Oracle SQLのFlashback Query構文であるAS OF TIMESTAMPが提示されています。
この構文を使うと、指定時刻以下で最新のIcebergスナップショットが選ばれます。
| 項目 | 公式ドキュメントの記載 |
|---|---|
| 対象 | Apache Iceberg外部表 |
| 過去の参照 |
AS OF TIMESTAMPで時刻を指定する |
| 選ばれる状態 | 指定時刻以下で最新のIcebergスナップショット |
| 通常の参照 |
AS OF TIMESTAMPを指定しない場合は、クエリ開始時点で現行のスナップショットを参照する |
| スナップショット一覧 |
DBMS_CLOUD.ICEBERG_TABLE_HISTORYでSNAPSHOT_IDとCOMMITTED_ATを取得する |
| 履歴の保持 | Oracle Flashback Time Travelの履歴保持管理は適用されず、Icebergのメタデータとストレージのライフサイクルに従う |
タイムトラベルの対応構成として、次の3つが明記されています。
- Databricks
- AWS Glue
- Hadoop Catalog on OCI Object Storage
同じページにはIceberg問い合わせ機能全体の対応構成も掲載されていますが、タイムトラベルについて明記されている構成は上記の3つです。
タイムトラベルで利用できる表の参照形式は、次のとおりです。
- ADBに作成したローカルIceberg外部表
- Icebergアクセス・プロトコルを使用するインライン外部表
- 接続元とリンクが必要なアクセスに対応している場合のCloud Link経由のIceberg表
また、時刻は特定のスナップショットIDを直接指定するものではありません。
例えば、12:20、12:40、13:00にスナップショットがある場合に、この選択規則を当てはめて12:50を指定すると、12:40のスナップショットが選ばれます。
今回使うデータ
データは、以下の記事で準備した環境をそのまま使って行います。
この環境では、上記ドキュメントに記載のあった対応カタログに該当していないAICAT(Oracle AI Data Catalog)を利用しています。AICATを経由したカタログ参照でもタイムトラベルできるのか試してみます。
少し細かいですが、前提になる利用データについて少し解説します。
使っているのは、ECサイトの注文データ1,071件です。
まず、注文を全件ACCEPTEDとして受け付けた状態をS0、そして、後から届いた情報を元に、キャンセルと配送遅延を反映した状態をS1としています。更新後、注文の合計は1,071件のままで、83件のステータスだけが変わっています。
| 状態 | コミット時刻 | ACCEPTED | CANCELED | DELIVERY_DELAYED | 合計 |
|---|---|---|---|---|---|
| S0(受付時点) | 2026-07-12 12:27:01.875 | 1,071 | 0 | 0 | 1,071 |
| S1(更新後) | 2026-07-12 12:47:31.653 | 988 | 12 | 71 | 1,071 |
S0とS1は、それぞれ独立したIcebergスナップショットとして残っています。
この検証では、S0がコミットされた後で、S1へ切り替わる前の2026-07-12 12:30:00を指定します。
実際のデータはObject Storage上にあり、ADBからは次の名前で参照します。
"demo"."orders_ochacafe_demo"@AICAT
demoはネームスペース、orders_ochacafe_demoはテーブル名、AICATはカタログ名です。
やってみる
では早速、タイムトラベルを試してみましょう!
0. 現在の注文ステータスを確認する
最初に、AS OF TIMESTAMPを付けずに現在のデータを集計します。
SELECT order_state,
COUNT(*) AS cnt
FROM "demo"."orders_ochacafe_demo"@AICAT
GROUP BY order_state
ORDER BY 1;
| ORDER_STATE | CNT |
|---|---|
| ACCEPTED | 988 |
| CANCELED | 12 |
| DELIVERY_DELAYED | 71 |
後から反映したキャンセルと配送遅延を含む、S1の状態が返りました。
これが最新のデータの状態です。
1. AS OF TIMESTAMP で更新前に戻る
では、同じテーブルにAS OF TIMESTAMPを追加します。
指定する12:30:00は、S0のコミット後かつS1のコミット前の時刻です。
SELECT order_state,
COUNT(*) AS cnt
FROM "demo"."orders_ochacafe_demo"@AICAT
AS OF TIMESTAMP TO_TIMESTAMP(
'2026-07-12 12:30:00.000000',
'YYYY-MM-DD HH24:MI:SS.FF6'
)
GROUP BY order_state
ORDER BY 1;
| ORDER_STATE | CNT |
|---|---|
| ACCEPTED | 1,071 |
更新前のS0が返り、1,071件すべてがACCEPTEDに戻りました。タイムスタンプ指定で、過去のスナップショットを参照できました!
最新のデータにはCANCELEDとDELIVERY_DELAYEDがあるはずですが、指定時刻では更新されていない状態なので、全てが受付済みに戻っています。
ADBは、指定時刻以下のIcebergスナップショットから最新のものを選んで参照します。
2. 過去と現在を1つの結果に並べる
タイムトラベルでは、過去の1時点を単独で参照するだけでなく、過去と現在など複数の時点を同じSQLで並べることもできます。
この例では、1つ目のSELECTがAS OF TIMESTAMP付きでS0を読み、2つ目のSELECTが時刻を指定せず現在のS1を読みます。
それぞれに表示用のphaseを付け、UNION ALLで1つの結果にまとめます。
SELECT '1.S0(注文受付時点)' AS phase,
order_state,
COUNT(*) AS cnt
FROM "demo"."orders_ochacafe_demo"@AICAT
AS OF TIMESTAMP TO_TIMESTAMP(
'2026-07-12 12:30:00.000000',
'YYYY-MM-DD HH24:MI:SS.FF6'
)
GROUP BY order_state
UNION ALL
SELECT '2.S1(後着情報反映後)',
order_state,
COUNT(*)
FROM "demo"."orders_ochacafe_demo"@AICAT
GROUP BY order_state
ORDER BY 1, 2;
| PHASE | ORDER_STATE | CNT |
|---|---|---|
| 1.S0(注文受付時点) | ACCEPTED | 1,071 |
| 2.S1(後着情報反映後) | ACCEPTED | 988 |
| 2.S1(後着情報反映後) | CANCELED | 12 |
| 2.S1(後着情報反映後) | DELIVERY_DELAYED | 71 |
S0では1,071件すべてがACCEPTEDでしたが、その後、S1では、そのうち12件がCANCELED、71件がDELIVERY_DELAYEDへ変わっています。
現在のデータだけを集計した場合はS1しか見えませんが、S0を同じ結果に含めると、後着情報を反映する前後の状態を一覧で確認できます。
タイムトラベルを使うと、事前に時点ごとのコピー表を作っていなくても、Icebergに履歴が残っている範囲で当時の集計を再現できます。
この形式は、障害の事後検証、問い合わせへの回答、対応経過の報告にも利用できます。
例えば、トラブル発覚時と対応後の時刻をそれぞれ指定すれば、「現在はどうなっているか」だけでなく、「発覚した時点ではどうなっていたか」も1つのSQL結果として提示できます。
(2つ目のSELECTは現在のスナップショットを読むため、S1より新しいスナップショットを追加した後は、その時点の最新状態が返ります。S1を固定して再現する場合は、S1を参照できる時刻もAS OF TIMESTAMPで指定します。)
Oracle AI Databaseでは、自然言語の入力を基にSQLを生成し、実行または説明するSelect AIという機能もありますので、過去と現在を比較するSQLを毎回利用者が組み立てなくても、問い合わせの入口を自然言語にできる点で、タイムトラベルによる履歴の活用範囲が広がります。
(※本記事ではSelect AIからAICAT上のIcebergテーブルに対してAS OF TIMESTAMPを含むSQLを直接生成できるかは検証していません。ここでは、履歴比較用のSQLやビューをあらかじめ用意した場合の活用例として紹介しています。)
注意事項
公式ドキュメントでは、参照時刻を決める前にDBMS_CLOUD.ICEBERG_TABLE_HISTORYを使い、利用可能なスナップショットを一覧表示できるコマンドが紹介されています。
SELECT snapshot_id,
committed_at
FROM TABLE(
DBMS_CLOUD.ICEBERG_TABLE_HISTORY('ICEBERG_ORDERS_DEMO')
)
ORDER BY committed_at;
しかし、今回の環境では次のエラーになりました。AICATを使ったことが原因かは断定できませんが、現時点の記録として書いておきます。
AS OF TIMESTAMPが読んでいる履歴
AS OF TIMESTAMPはOracle SQLのFlashback Queryで使われる構文です。
ただし、今回読んでいる過去の状態は、ADB内部のUNDOに保存されたものではありません。
ADBは指定時刻からIcebergスナップショットを選び、そのスナップショットが参照するマニフェストとデータファイルをObject Storageから読みます。
| 項目 | 今回の動き |
|---|---|
| SQLの構文 | Oracle SQLのAS OF TIMESTAMP
|
| 過去の状態 | Icebergのスナップショット |
| 実データ | Object Storage上のデータファイル |
| 履歴の保持 | Icebergのメタデータとストレージのライフサイクルで管理 |
そのため、過去のデータをどこまで参照できるかは、Iceberg側の履歴の保持状況で決まります。
利用時の制限
以下は、Oracleの公式ドキュメントに記載されている制限です。
(今回の検証では、これらのエラー条件までは実機確認していません。)
- 履歴ファイル:参照するスナップショットのメタデータ、マニフェスト、データファイルが残っている必要がある
- 履歴の範囲:ADBから利用できるIceberg履歴より前の時刻を指定するとエラーになる
- スキーマ:参照するテーブルと過去のスナップショットのスキーマに互換性が必要
- 更新方式:Icebergメタデータがmerge-on-readのdelete fileを参照している場合、クエリは失敗する
- パーティション:現在の制限では、非パーティションのIcebergテーブルが対象
- 時刻の精度:同じ3秒の時刻範囲で複数のスナップショットが作られると、想定より前のスナップショットが選ばれることがある
注意点:
AS OF TIMESTAMPは、Icebergの履歴をADB側に新たに保持する機能ではありません。
Iceberg側で古いスナップショットやデータファイルを削除すると、ADBからもその時点を参照できなくなります。
おわりに
公式ドキュメントでは、Icebergのタイムトラベル対応構成としてDatabricks、AWS Glue、OCI Object Storage上のHadoop Catalogが明記されています。
今回利用したAICATはその一覧に含まれていませんが、AICAT経由のIcebergテーブルにAS OF TIMESTAMPを追加し、ADBから更新前のS0を参照できました。
今後もADBのIceberg対応に期待です!

