dbt macrosでBigQuery上に簡易的なダミーデータセットを生成する

:::note info
本記事は [ZOZO Advent Calendar 2023](https://qiita.com/advent-calendar/2023/zozo) シリーズ 7 の 19 日目の記事です。
:::

# 概要
dbt-core(OSS版)を使う場合、本番環境と Sandbox 環境の分離については自身で検討する必要があります。
今回を以下を前提に開発フローを整備しました。
- データベースとして BigQuery を利用
- それぞれ GCP プロジェクトを用意することで環境分離
- Sandbox 環境からは**直接**本番環境を source として参照しない[^1]

その際、Sandbox 環境上での開発者体験を向上させるために CLI コマンド1つで source として利用するためのデータセットを生成できると便利でした。
本記事は本番環境のテーブルを参照して、 Sandbox 環境にビューとして実体化する dbt macros の実装について紹介します。

# dbt macros とは
dbt では再利用可能な関数として dbt macros を定義することができます。
dbt macros は各 dbt モデルを定義する SQL から呼び出すことも、 `dbt run-operation`コマンドを使って CLI から直接呼び出すこともできます。

また dbt は SQL とテンプレート言語の Jinja を組み合わせて柔軟に記述することができますが、 dbt macros でもこの Jinja をうまく使って表現することになります。
dbt macros に関する公式のドキュメントはこちらです。

https://docs.getdbt.com/docs/build/jinja-macros#macros

# 実装方法
`macros`ディレクトリ配下に dbt macros を3本用意します。
`create_development_views`：メインメソッドの役割を担う
`create_development_dataset`：Sandbox 環境で開発用のビューを格納するためのデータセットを作成する
`get_tables`：INFORMATION_SCHEMA.TABLES を利用して引数として指定したデータセット配下のテーブル一覧を取得する

 - `project-id-of-production`は実際に参照する本番環境のプロジェクトIDに置き換えてください。
 - `create_development_views.sql`8行目の`dataset_name_a`,`dataset_name_b`は実際に参照する本番環境のデータセット名に置き換えてください。

```sql:create_development_views.sql
# 開発用のビューを作成する
{% macro create_development_views() %}

# デプロイ先となるデータセットを作成する
{% do create_development_dataset() %}

# 参照元となるデータセットを設定する
{% set reference_datasets = ['dataset_name_a','dataset_name_b'] %}

# データセット内のテーブル一覧を取得する 
{% set results = get_tables(reference_datasets) %}

{% if execute %}
  {% set tables = results.rows %}
  {% for table in tables %}

    # 本番環境のテーブルを参照したビューを作成する
    {% set create_view %}
    create or replace view `{{ database }}`.`{{ generate_schema_name() }}`.`{{ table.table_name }}`
      as (
        select * 
        from `project-id-of-production`.`{{ table.table_schema }}`.`{{ table.table_name }}`
      )
    {% endset %}
    {% do run_query(create_view) %}
  {% endfor %}
{% endif %}

{% endmacro %}
```

```sql:create_development_dataset.sql
# 開発用のデータセットを作成する
{% macro create_development_dataset() %}

{% set create_development_dataset %}
create schema if not exists `{{ database }}`.`{{ generate_schema_name() }}`;
{% endset %}

{% do run_query(create_development_dataset) %}

{% endmacro %}
```

```sql:get_tables.sql
# テーブル一覧を取得する
{% macro get_tables(reference_datasets) %}

# INFORMATION_SCHEMA.TABLES を利用して指定したデータセット配下のテーブル名を取得する
{% set get_tables %}
{% for reference_dataset in reference_datasets %}
    select
        table_schema,
        table_name
    from
        `project-id-of-production`.`{{ reference_dataset }}`.INFORMATION_SCHEMA.TABLES
    {% if not loop.last %}
        union all
    {% endif %}
{% endfor %}
{% endset %}
 
{% set results = run_query(get_tables) %}
{{ return(results) }}

{% endmacro %}
```

CLI で`dbt run-operation create_development_views`コマンドを実行すると Sandbox 環境にデータセットが生成されます。
ダミーデータセットというからには当然ダミー値で置き換えたいケースもあると思いますが、その場合はカラム名を取得する dbt macros をもう一本用意して`create_development_tables.sql`21行目の  select 句内を for 文で回して定義する形になります。


:::note warn
(2023/12/20追記)動作確認を行った環境は以下です。
dbt-core: 1.5.2
dbt-bigquery: 1.5.3
:::


# まとめ
本記事ではdbt macros を使って CLI コマンド1つで source として利用するためのダミーデータセットを生成する方法を紹介しました。
Sandbox 環境上での開発者体験を向上させるための工夫については、まだまだ改善の余地があります。
いつかまたもう少しブラッシュアップした内容を紹介できればと思います。

[^1]: 色々調べているとこの要件自体が特殊なのかもしれません。dbt の仕組み上は source として定義しているものに対して変更を加えることはまずないので。ただ今回はこの要件を前提にdbt macros開発をしてみました。カスタマイズ内容によっては他のユースケースにも対応ができるかもしれません。