slowly changing dimensions
SCD
#dbt
ECサイトなどで、価格が変化した場合、商品名を変更した場合、
売上数などに影響がある。
なので、時間の項目を付与したその時点の値を保存して、
code: desc.txt
なるほど、行単位で変更を検知して、変更行を日付付きで保存する。
⏺ まさにその通りです!行単位での変更検知と日付付きでの履歴保存が、dbt snapshotの核心部分です。
★ Insight ─────────────────────────────────────
この「行単位変更検知」は、データベースのSCDタイプ2(Slowly Changing Dimension Type
2)を自動化したものです。手動実装では複雑になりがちなロジックを、dbtが宣言的に処理してくれる優
れた仕組みです。
─────────────────────────────────────────────────
具体的な変更検知の仕組み
変更検知の戦略
dbtは2つの主要な戦略を提供します:
1. check戦略 - 指定カラムの値変更を検知
{{ config(
strategy='check',
check_cols='price', 'category', -- これらのカラムが変更されたら記録
unique_key='sku'
) }}
2. timestamp戦略 - 更新日時カラムを監視
{{ config(
strategy='timestamp',
updated_at='updated_at', -- このカラムが新しくなったら記録
unique_key='sku'
) }}
実際の変更検知プロセス
ステップ1: 現在のデータ状態
-- 元データ(今日)
sku | name | price | category
001 | Tシャツ | 2980 | メンズ
002 | パンツ | 4980 | メンズ
003 | スカート | 3980 | レディース
ステップ2: 明日、価格とカテゴリが変更
-- 元データ(明日)
sku | name | price | category
001 | Tシャツ | 3480 | メンズ -- 価格変更 ✓
002 | パンツ | 4980 | メンズ -- 変更なし
003 | スカート | 3980 | ウィメンズ -- カテゴリ変更 ✓
ステップ3: dbt snapshotの変更検知結果
-- snapshot実行後のテーブル
sku | name | price | category | dbt_valid_from | dbt_valid_to
001 | Tシャツ | 2980 | メンズ | 2024-08-14 | 2024-08-15 -- 終了
001 | Tシャツ | 3480 | メンズ | 2024-08-15 | NULL -- 新規
002 | パンツ | 4980 | メンズ | 2024-08-14 | NULL -- 継続
003 | スカート | 3980 | レディース | 2024-08-14 | 2024-08-15 -- 終了
003 | スカート | 3980 | ウィメンズ | 2024-08-15 | NULL -- 新規
内部的な処理ロジック
dbtは以下のような内部処理を自動実行します:
-- dbtが内部的に実行する処理(概念)
WITH current_data AS (
SELECT sku, name, price, category FROM source_table
),
existing_snapshot AS (
SELECT * FROM snapshot_table WHERE dbt_valid_to IS NULL
),
changes_detected AS (
SELECT
c.*,
CASE
-- check_colsに変更があるかチェック
WHEN c.price != e.price OR c.category != e.category
THEN 'CHANGED'
WHEN e.sku IS NULL
THEN 'NEW'
ELSE 'UNCHANGED'
END as change_type
FROM current_data c
LEFT JOIN existing_snapshot e USING (sku)
)
-- 1. 変更された行の古いレコードを終了
UPDATE snapshot_table
SET dbt_valid_to = CURRENT_DATE
WHERE sku IN (SELECT sku FROM changes_detected WHERE change_type = 'CHANGED');
-- 2. 新規・変更行を挿入
INSERT INTO snapshot_table
SELECT
*,
CURRENT_DATE as dbt_valid_from,
NULL as dbt_valid_to
FROM changes_detected
WHERE change_type IN ('NEW', 'CHANGED');
変更検知の粒度制御
細かい変更を無視したい場合
{{ config(
strategy='check',
check_cols='price', 'category', -- nameの変更は無視
unique_key='sku'
) }}
すべての変更を検知したい場合
{{ config(
strategy='check',
check_cols='all', -- 全カラムの変更を検知
unique_key='sku'
) }}
このプロジェクトでの実用例
アパレルプロジェクトなら、こんな変更パターンが重要でしょう:
-- snapshots/sku_master_snapshot.sql
{{ config(
strategy='check',
check_cols='price', 'retail_price', 'category', 'product_class_1', 'product_class_2',
unique_key='sku'
) }}
SELECT
sku,
name,
price,
retail_price,
category,
product_class_1,
product_class_2,
color,
size_name
FROM {{ ref('stg_raw_sku_master') }}
これにより:
- 価格変更: セール開始/終了
- カテゴリ変更: 商品分類の見直し
- 商品クラス変更: マーケティング戦略変更
が行単位で日付付きで記録され、売上への影響分析が可能になります。
まさに「いつ」「何が」「どう変わったか」を、行レベルで克明に記録する仕組みですね!