🍊

BigQuery のテーブル名が一意でない状態で dbt を導入する

2024/09/17に公開

はじめに

  • dbt のモデル名が一意である必要があり、dbtのモデル名と対応するBigQuery のテーブル名も同様に一意にする必要があると思っていた。
  • それだと、dbt 導入のために既存のデータ分析基盤にあるBigQuery のテーブル名を全て変更する必要があり手間がかかるので回避策を探った。

↓モデル名が一意ではないのでNG

既存のBigQuery 構成
プロジェクトID
├─ dwh_ads(データセット)
│  ├─ ga4(テーブル)
│  └─ yahoo(テーブル)
└─ mrt_ads(データセット)
   ├─ ga4(テーブル)
   └─ yahoo(テーブル)
dbt のディレクトリ構成(既存のBigQuery 構成と同じにしたかった)
models
├─ dwh_ads(ディレクトリ)
│  ├─ ga4.sql(モデル: ga4)    # mrt_ads.ga4と重複する
│  └─ yahoo.sql(モデル: yahoo) # mrt_ads.yahooと重複する
└─ mrt_ads(ディレクトリ)
   ├─ ga4.sql(モデル: ga4)    # dwh_ads.ga4と重複する
   └─ yahoo.sql(モデル: yahoo) # dwh_ads.yahooと重複する
  • 本記事では、既存のデータ分析基盤にあるBigQuery のテーブル名を変更せずに dbt を導入する上で実施したことを記載する。

動作環境

  • dbt core
Running with dbt=1.8.3
Registered adapter: bigquery=1.8.0

BigQuery 構成(既存データ分析基盤)

  • 既存の構成ルールは
    • データセットをデータレイヤー✖️データソースごとに分ける
    • データセット名は、{データレイヤー}_{データソースカテゴリ}で命名
    • テーブル名で一意になっていない。当初のdbt導入の障壁
BigQuery 構成
プロジェクトID
├─ dwh_ads(データセット)
│  ├─ ga4(テーブル)
│  └─ yahoo(テーブル)
└─ mrt_ads(データセット)
   ├─ ga4(テーブル)
   └─ yahoo(テーブル)

BigQuery 構成に対応する dbt の設定

dbt のディレクトリ構成

  • BigQuery 構成(既存データ分析基盤)に変更を加えず、dbt 側の設定で対応する。
  • BigQuery のデータセット、テーブルのツリー構造と同様に dbt のディレクト構成を設定した。
    → 保守しやすいため、同じツリー構造にした。
dbt のディレクトリ構成
models
├─ dwh_ads(ディレクトリ)
│  ├─ dwh_ads__ga4.sql(モデル: dwh_ads__ga4、エイリアス: ga4)
│  └─ dwh_ads__yahoo.sql(モデル: dwh_ads__yahoo、エイリアス: yahoo)
└─ mrt_ads(ディレクトリ)
   ├─ mrt_ads__ga4.sql(モデル: mrt_ads__ga4、エイリアス: ga4)
   └─ mrt_ads__yahoo.sql(モデル: mrt_ads__yahoo、エイリアス: yahoo)
  • モデル名:dbtのクエリファイル名(一意である必要がある)
  • エイリアス名:Bigqueryに作成するテーブル名
    • モデル名と重複はNG(エイリアス: yahooがある場合、モデル名でyahooが使えない)
    • 基本このルールで書いていたら重複しないと思う

SQLのファイル名(dbtのモデル名)を{データレイヤー}_{データソースカテゴリ}__{データソース}に設定することにより、一意のモデル名が実現できた。
上記のdbt 側のディレクトリ構成を実現するために必要な各種設定を解説する。

Macros の設定

dbt のディレクトリ名を BigQuery のデータセット名に設定する

  • dbt : dwh_ads(ディレクトリ)
    ↓↓↓
  • BigQuery : dwh_ads(データセット)
macros/generate_schema_name.sql
{% macro generate_schema_name(custom_schema_name, node) -%}
   {%- if custom_schema_name is none -%}
       {{ target.schema }}
   {%- else -%}
       {{ custom_schema_name | trim }}
   {%- endif -%}
{%- endmacro %}
dbt_project.yml
models:
  dbt_project:        # 任意のプロジェクト名
    dwh_ads:         # データセット名記載
      +materialized: table # 任意
      +schema: dwh_ads     # データセット名記載

https://docs.getdbt.com/docs/build/custom-schemas

dbt のディレクトリ名とモデル名からカスタムエイリアス名を設定する

  • dbt
    • dwh_ads(ディレクトリ)
      • dwh_ads__ga4.sql(モデル: dwh_ads__ga4、エイリアス: ga4)
        ↓↓↓
  • BigQuery
    • dwh_ads(データセット)
      • ga4(テーブル)
macros/generate_schema_name.sql
{% macro generate_alias_name(custom_alias_name, node) -%}
    {%- if custom_alias_name -%}
        {{ custom_alias_name | trim }}
    {%- else -%}
        {%- set schema_name = node.schema -%}
        {%- set model_name = node.name -%}
        {%- set schema_prefix = schema_name ~ "__" -%}
        {%- if model_name.startswith(schema_prefix) -%}
            {{ model_name[schema_prefix|length:] }}
        {%- else -%}
            {{ model_name }}
        {%- endif -%}
    {%- endif -%}
{%- endmacro %}

dwh_ads__ga4のモデル名からdwh_ads__を外してga4をエイリアスに設定する処理

  • 引数:
    • custom_alias_name:カスタムエイリアス名が指定されている場合にその値を使用します。
    • node:dbtのモデルノード。ここからモデル名を取得します。
    • target:ターゲットのスキーマ情報を含むオブジェクト。スキーマ名とモデル名を結合する際に使用されます。
  • custom_alias_name=none は、引数 custom_alias_name の初期化を意味します。これは、関数やマクロが呼び出されたときに custom_alias_name に値が渡されなかった場合のデフォルト値として none を設定することを示しています。
  • {{ custom_alias_name | trim }}:その値をトリム(前後の空白を削除)して返します。
  • Configで設定することも可能(参考情報)
Model Config Detabase Identifier
ga_sessions.sql なし "analitics"."ga_sessions"
ga_sessions.sql {{ config(alias=sessions) }} "analytics"."sessions"

https://docs.getdbt.com/reference/resource-configs/alias
https://docs.getdbt.com/docs/build/custom-aliases

参考

https://qiita.com/takagia/items/b1f49f1b32ec289c2512
https://zenn.dev/gak_t12/articles/34bceec8492019
https://docs.getdbt.com/reference/resource-configs/alias
https://docs.getdbt.com/docs/build/custom-aliases
https://docs.getdbt.com/docs/build/custom-schemas

Discussion