🎃

【dbt Docs】Building a dbt Project - Snapshots

2022/03/15に公開

Snapshots

https://docs.getdbt.com/docs/building-a-dbt-project/snapshots

  • Snapshot configurations

    • resource-path

    • target_schema

    • target_database

    • unique_key

    • strategy

    • updated_at

    • check_cols

    • dbt_project.ymlの snapshots:

    • .sql config ブロック

    • .ymlの resource property

    snapshots/postgres_app/orders_snapshot.sql
    {% snapshot orders_snapshot %}
        {{
        	config(
             unique_key='id',
        	  strategy='timestamp',
          	updated_at='updated_at'
     	   )
    	}}
    	-- Pro-Tip: Use sources in snapshots!
    	select * from {{ source('jaffle_shop', 'orders') }}
    {% endsnapshot %}
    
  • Snapshot properties

    • snapshots/ディレクトリ、snapshot-pathsで設定も可能
    • models/
  • snapshot command

    $ dbt snapshot --help
    

Getting started(はじめに)

What are snapshots?( snapshotsとは)

分析者はデータの中身が変更するテーブルを、以前のある時点にさかのぼって確認したい場合が多々ある。データソース側で過去のある時点のデータを見れるようにしている場合もあるけど、そうでない場合も多い。dbtはsnapshotという機能を提供している。これは変更内容を記録しておく機構

Snapshots機能は、type-2 Slowly Changeing Dimensions(SCDs)を元に実装されている。
下記のような変更を記録する( dbt_valid_from, dbt_valid_to )

id status updated_at
1 pending 2019-01-01
id status updated_at
1 shipped 2019-01-02
id status updated_at dbt_valid_from dbt_valid_to
1 pending 2019-01-01 2019-01-01 2019-01-02
2 shipped 2019-01-02 2019-01-02 null

dbtでは、 snapshotsは、select句で記載される。snapshots/に配置される.sqlに置かれる場合が多い。

snapshots/orders.sql
{% snapshot orders_snapshot %}

{{
    config(
      target_database='analytics',
      target_schema='snapshots',
      unique_key='id',

      strategy='timestamp',
      updated_at='updated_at',
    )
}}

select * from {{ source('jaffle_shop', 'orders') }}

{% endsnapshot %}

dbt snapshot実行時の挙動としては

  • 最初の起動時 まずスナップショットテーブルを作成する。( select句の実行そのまま ) dbt_valid_fromdbt_valid_toが追加される。dbt_valid_to = nullがまず入る
  • 以降の実行時 変更されたレコードと新規レコードをチェックする。
    • 変更された場合は、 dbt_valid_toも更新される
    • 新しいレコードが入った場合は、挿入される。 dbt_valid_to = nullとなる

Example

プロジェクトにスナップショットを追加するためには

  • snapshotsディレクトリに、.sqlファイルを作成する 例) snapshots/orders.sql
  • blockに snapshotの定義を記載する
    snapshots/orders.sql
    {% snapshot orders_snapshot %}
    ...
    {% endsnapshot %}
    
  • select句を記載する
    snapshots/orders.sql
    {% snapshot orders_snapshot %}
    
    select * from {{ source('jaffle_shop', 'orders') }}
    
    {% endsnapshot %}
    
  • クエリの結果セットに、レコードが最後に更新された日時を示す信頼できるタイムスタンプ列が含まれているかどうかを確認します。 この例の場合、updated_atを見れば更新の判断はできるので、timestampを使っての判断ができる。updated_atなどtimestampがない場合は、checkを使って判断をすることができる。
  • blockを使用して設定をすることもできる。(個別の.sqlファイルでも, dbt_project.ymlでも)
    snapshots/orders.sql
    {% snapshot orders_snapshot %}
    
    {{
        config(
          target_database='analytics',
          target_schema='snapshots',
          unique_key='id',
    
          strategy='timestamp',
          updated_at='updated_at',
        )
    }}
    
    select * from {{ source('jaffle_shop', 'orders') }}
    
    {% endsnapshot %}
    
  • dbt snapshot コマンドを実行する。この例では、
    analytics.snapshots.orders_snapshotテーブルが作成される。
  • 作成されたテーブルを確認する
  • 念の為 dbt snapshotを実行して、変更追加が反映されるか確認する
  • ref関数で、他のモデルから参照することができる
    models/changed_orders.sql
    select * from {{ ref('orders_snapshot') }}
    
  • dbt snapshot コマンドを定期実行する。( cronとかで ) snapshotは、定期的に頻繁に実行すると役に立つ。

Detecting row changes

データがどう変更されたか?をチェックする方法が2種類用意されている。 timestampcheck

Timestamp strategy (recommended)

(タイムスタンプ戦略:おすすめ)

更新日時のカラムを元に判断する。updated_at

snapshots/timestamp_example.sql
{% snapshot orders_snapshot_timestamp %}

    {{
        config(
          target_schema='snapshots',
          strategy='timestamp',
          unique_key='id',
          updated_at='updated_at',
        )
    }}

    select * from {{ source('jaffle_shop', 'orders') }}

{% endsnapshot %}

Check strategy

更新日時といったカラムがない場合に、こちらを使う。指定のカラムが過去と変更があるかないか?を比較することで機能する。

構成 説明
check_cols all 全部のカラム変更を確認する、またはすべての列を確認する列のリスト ["name", "email"]
snapshot/check_example.sql
{% snapshot orders_snapshot_check %}

    {{
        config(
          target_schema='snapshots',
          strategy='check',
          unique_key='id',
          check_cols=['status', 'is_cancelled'],
        )
    }}

    select * from {{ source('jaffle_shop', 'orders') }}

{% endsnapshot %}

Hard deletes (opt-in)

ソースクエリから削除された行は、デフォルトでは無効化されない。
invalidate_hard_deletesを使えば追跡可能。

Configuring snapshots

Snapshot configurations

  • target_database
  • target_schema
  • stragegy timesatamp or check
  • unique_key
  • check_cols : checkの際に判断対象となるカラム名のリスト
  • updated_at : timestampの際に判斷対象となるtimestamp列名
  • invalidate_hard_deletes : ソースデータのカラム削除に追従するか

Configuration best practices (構成のベストプラクティス)

  • strategy : timestamp を使う
  • unique_keyが本当にユニーク化確認する
  • 分析用のスキーマとは別のスキーマを指定する
    • スナップショットは再構成ができないので、権限的にも簡単に削除や変更ができないように権限設定をしておくほうが良い

Snapshot query best practices

  • ソースデータのスナップショットを取る
  • source関数を使う。直書きしない、リネージュを追えるようにも
  • できるだけ多くの列を含む。実際パフォーマンスが許すなら  select * を使うことを推奨
  • スナップショットではJOINは使わない
  • 変換は極力行わない(ソースデータだけ取ろうね) ビジネスロジックは変更になりやすいので

Snapshot meta-fields

  • dbt_valid_from 最初の挿入時のタイムスタンプ
  • dbt_valid_to 無効になった時のタイムスタンプ
  • dbt_scd_id 生成された一意キー、dbt内部で使う
  • dbt_updated_at 行が挿入された時のソースレコードのupdated_at、dbt内部で使う

FAQs

  • 一度に1つのスナップショットを実行するにはどうすればよいですか?
    $ dbt snapshot --select order_snapshot
    
  • スナップショットコマンドを実行する頻度はどれくらいですか?

    1時間毎、1日毎 などに取ることを推奨。それ以下の場合は別の手段を検討してください。

  • スナップショットクエリに新しい列を追加するとどうなりますか?

    追加カラムは追加される。ただ、削除カラムはデフォルトでは削除しない

  • フックはスナップショットで実行されますか?

    pre-hook, post-hook, on-run-start, on-ran-endで使用できる。

  • スナップショット用の`target_schema`が1つしかないのはなぜですか?
  • プロジェクトの`snapshot`ディレクトリ以外のディレクトリにスナップショットを保存できますか?

    デフォルトでは snapshots/ディレクトリ。snapshot-pathsで設定することも可能

Discussion