dbt を使おうとすると、profile や target、config、property など、様々な概念が出てくる。
それらをあまり理解できていなくても、何となく動かすことはできるかもしれない。
しかし、これらの概念を理解していないと、意図した通りに動かしたり他者が記述した設定内容を理解したりするのは難しい。
この記事では、最初は理解が難しいこれらの概念について、「model の出力先の設定」を題材にして説明していく。
dbt run
した際にどこにテーブルやビューが出力されるのか、設定を読み解いて理解できるようになることを目指す。
この記事の内容は dbt のバージョン1.8.5
で動作確認している。
データウェアハウスは BigQuery を使用する。
概要
どのデータウェアハウスのどのテーブルにあるデータを使うのか、データの出力先はどこになるのか。
そういった設定には「profile による基本設定」と「config による個別設定」の 2 つがあり、個別設定によって基本設定を上書きすることができる。
この仕組みによって、特定の model についてだけ出力先を変える、といったことが可能になる。
まずは基本設定から見ていく。
基本設定
dbt では profile という概念によって、データウェアハウスとの接続に関する情報を扱う。
そしてその profile について記述するためのファイルが、profiles.yml
。このファイルに、どのデータウェアハウスを使うのか、どのデータベースを使うのか、といったことを記述していく。
以下は、データウェアハウスとして BigQuery を使い、dbt-project-a-432612
というプロジェクトのdest
データセットに model を出力する場合のprofiles.yml
。
my_profile_1: # profile の名前 target: dev # デフォルトで使う target の名前 outputs: dev: # target の名前 type: bigquery # 接続先のデータウェアハウスの種類 method: oauth # 認証に使う方法 project: dbt-project-a-432612 # 使用するプロジェクト dataset: dest # 使用するデータセット
ひとつの profile は、ひとつ以上の target を持つ。データウェアハウスについての具体的な情報は target に書く。target はprofiles.yml
の<profile-name>.outputs
に書いていく。
上記の例では、dev
という target を定義している。
詳細は後述するが、ひとつの profile のなかで複数の target を定義できるため、デフォルトで使う target を指定しておくことができる。profiles.yml
の<profile-name>.target
で指定する。
上記の例ではtarget: dev
がそれにあたる。
上記の例では target で 4 つの項目を設定している。
このうちmethod
は認証に関するものであり、この記事の内容との関係は薄いので割愛する。
詳細は、公式ドキュメントの以下のページで確認できる。
https://docs.getdbt.com/docs/core/connect-data-platform/bigquery-setup
重要なのはproject
とdataset
である。これで、接続先を指定している。
項目名はデータウェアハウスによって異なるので注意する。例えば Snowflake の場合はdatabase
やschema
を使って設定を行う。
https://docs.getdbt.com/reference/dbt-jinja-functions/target
project
は、データの出力先として使われるだけでなく、source の参照先としても使われる。
例えば以下のように source を定義してfrom {{ source('src', 'user') }}
とする場合、dbt-project-a-432612
プロジェクトのsrc
データセットのuser
テーブルが使われる。
version: 2 sources: - name: src # dataset を省略した場合は name が dataset として使われる tables: - name: user columns: - name: id - name: name
dbt-project-a-432612.src.user
を用意し、それを参照しているfoo
という model を書いて$ dbt run -s foo
を実行してみると……。
エラーになる。
$ dbt run -s foo 15:48:52 Running with dbt=1.8.5 15:48:52 Encountered an error: Runtime Error No dbt_project.yml found at expected path <カレントディレクトリのパス>/dbt_project.yml Verify that each entry within packages.yml (and their transitive dependencies) contains a file named dbt_project.yml
dbt run
を実行するためには、dbt_project.yml
を用意する必要がある。そして、dbt_project.yml
で、どの profile を使うか指定しなければならない。
name: 'my_dbt_project' config-version: 2 version: '1.0.0' profile: 'my_profile_1' # profiles.yml で定義した my_profile_1 を指定
name
とversion
に、この dbt プロジェクトの名前とバージョンを指定する。config-version
は2
にする決まり。
そしてprofile
キーに、使用する profile を指定する。
この状態で$ dbt run -s foo
すると今度は上手くいき、dbt-project-a-432612.dest.foo
というビューが作られる。
target の使い分け
ひとつの profile に対して、複数の target を定義できる。
以下の例では、先ほど定義したdev
に加えて、prod
という target を定義した。そしてprod
ではdbt-project-b-432615
プロジェクトを使うと定義している。
my_profile_1: # profile の名前 target: dev # デフォルトで使う target の名前 outputs: dev: # target の名前 type: bigquery method: oauth project: dbt-project-a-432612 dataset: dest prod: # target の名前 type: bigquery method: oauth project: dbt-project-b-432615 dataset: dest
dbt run
する際に--target
フラグを使うことで、使用する target を指定できる。
例えば$ dbt run -s foo --target prod
を実行すると、dbt-project-b-432615.dest.foo
ビューが作られる。その際はもちろん、dbt-project-b-432615.src.user
が source として使われる。
--target
フラグを使わなかった場合は、<profile-name>.target
で指定した target (上記の例だとdev
)が使われる。
--target
フラグを使わず<profile-name>.target
も定義されていなかった場合はエラーになる。
$ dbt run -s foo 16:57:30 Running with dbt=1.8.5 16:57:30 target not specified in profile 'my_profile_1', using 'default' 16:57:30 Encountered an error: Runtime Error The profile 'my_profile_1' does not have a target named 'default'. The valid target names for this profile are: - dev - prod
config
model 毎に、その出力先を設定することができる。
その仕組みを理解するためにはまず、config という概念を理解する必要がある。
model に対しては property を設定できる(model だけでなく seed や test などにも property を設定できるが、この記事では割愛する)。
そして property の中には、 config と呼ばれる特殊な property がある。
config は以下の 3 つの方法で設定できる。
config()
関数- 個別の model について記述した
.yml
ファイル dbt_project.yml
上記の順番で優先度が高く、ひとつの config に対して複数の方法で設定されていた場合、優先度が高い方法で設定した内容が採用される。
詳細はこれから具体例を用いて説明していくので、config には複数の設定方法がある、優先順位が決まっている、ということをまずは覚えておけばよい。
model に設定できる config のうち、model の出力先に関係するのがdatabase
とschema
。
BigQuery の場合はproject
とdataset
という config も設定できる。それぞれ、database
とschema
と互換性がある。
BigQuery の用語(プロジェクトとデータセット)と合わせたほうが分かりやすいので、この記事ではproject
とdataset
を使うことにする。
dbt_project.yml で config を設定する
まずはdbt_project.yml
でproject
config を使ってみる。
name: 'my_dbt_project' config-version: 2 version: '1.0.0' profile: 'my_profile_1' models: my_dbt_project: # name で指定した project の名前を書く marts: # marts ディレクトリ以下にある model は…… +project: dbt-project-x # dbt-project-x に出力される
model に対する config はmodels:
の下に書いていく。まずは project の名前を書き、その下に具体的な設定を書いていく。
今回はmarts
ディレクトリ以下の model に対して config を設定したいので、まずはmarts
と書く。そして、+<config名>: 設定内容
を書く。上記の例ではproject
config にdbt-project-x
を設定している。
こうすると、marts
ディレクトリ以下にある model はdbt-project-x
に出力されるようになる。
例えばmodels/
の中身が以下のときに$ dbt run --target dev
すると、dbt-project-a-432612.dest.foo
とdbt-project-x.dest.bar
が作られる。
models ├── foo.sql ├── marts │ └── bar.sql └── sources.yml
target としてdev
を指定したので、profiles.yml
に記した内容に基づき、接続先はdbt-project-a-432612.dest
になる。
しかしmarts
以下のディレクトリについてはdbt-project-x
に出力するようにdbt_project.yml
に記したので、その設定が優先される。dataset
についてはdbt_project.yml
で特に設定していないので、dev
で設定したdest
がそのまま使われる。
そのため、marts/bar.sql
の実行結果がdbt-project-x.dest.bar
に書き込まれるのである。
次はdataset
も使ってみる。
dbt_project.yml
に+dataset: my_suffix_1
を書き加える。
name: 'my_dbt_project' config-version: 2 version: '1.0.0' profile: 'my_profile_1' models: my_dbt_project: # name で指定した project の名前を書く marts: # marts ディレクトリ以下にある model は…… +project: dbt-project-x # dbt-project-x に出力される +dataset: my_suffix_1 # target.dataset に _my_suffix_1 という接尾辞をつけたデータセットに出力される
この状態で$ dbt run --target dev
すると、dbt-project-a-432612.dest.foo
とdbt-project-x.dest_my_suffix_1.bar
が出力される。
my_suffix_1
というデータセットに出力されるのではなく、target.dataset
(今回の場合dest
)とmy_suffix_1
を_
でつないだデータセットに出力されるので、注意が必要。
model のproject
やdataset
config で設定しているのはあくまでも、「model の出力先の設定」である。「source としてどのデータベースを参照するか」には影響を与えない。
そのためのfoo
もbar
も、from {{ source('src', 'user') }}
している場合はdbt-project-a-432612.src.user
を参照するので注意する。
config を入れ子にする
以下のように config をネストさせていくことも可能。
name: 'my_dbt_project' config-version: 2 version: '1.0.0' profile: 'my_profile_1' models: my_dbt_project: # name で指定した project の名前を書く marts: # marts ディレクトリ以下にある model は…… +project: dbt-project-x # dbt-project-x に出力される +dataset: my_suffix_1 # target.dataset に _my_suffix_1 という接尾辞をつけたデータセットに出力される special: # marts/special ディレクトリ以下にある model は…… +dataset: my_suffix_2 # target.dataset に _my_suffix_2 という接尾辞をつけたデータセットに出力される
このように書くと、marts/special
ディレクトリ以下にある model はdest_my_suffix_2
データセットに出力されるようになる。
marts/special/baz.sql
を用意して確認してみる。
models ├── foo.sql ├── marts │ ├── bar.sql │ └── special │ └── baz.sql └── sources.yml
この状態で$ dbt run --target dev
すると、以下のビューが作られる。
dbt-project-a-432612.dest.foo
dbt-project-x.dest_my_suffix_1.bar
dbt-project-x.dest_my_suffix_2.baz
special
で設定したのはdataset
のみなので、project
はmarts
で設定したdbt-project-x
がmarts/special/baz.sql
にも適用される。
個別の model について記述した .yml ファイルで config を設定する
model と同じディレクトリに.yml
ファイルを用意し、そこに個別の model に対する property を記述することができる。
config も property の一部なので、project
やdataset
もそのファイルに書くことができる。
例として、models/marts/special/qux.sql
という model を用意し、その model について記述したmodels/marts/special/qux.yml
を用意する。
models/marts/special/qux.yml
には以下の内容を書く。
version: 2 models: - name: qux config: project: dbt-project-y
qux
のproject
をdbt-project-y
にしている。
dataset
は特に上書きしていないので、dbt_project.yml
の内容がそのまま使われる。qux.sql
はmarts/special
以下にあるので、今回の例だとmy_suffix_2
になる。
その結果、models/marts/special/qux.sql
の内容はdbt-project-y.dest_my_suffix_2.qux
に出力される。
このように、個別の model について記述した.yml
ファイルで設定した内容は、dbt_project.yml
の内容よりも優先される。
config() 関数で config を設定する
最後に、最も優先順位が高いconfig()
関数を使った方法について述べる。
先ほど用意したmodels/marts/special/qux.sql
の内容を以下のようにする。
{{ config( project='dbt-project-z' ) }} select name from {{ source('src', 'user') }}
そうすると、出力先はdbt-project-z.dest_my_suffix_2.qux
になる。
このように、.yml
で定義した内容(今回の例だとproject: dbt-project-y
)よりも、config()
関数の内容(今回の例だとproject='dbt-project-z'
)が優先される。
generate_database_name と generate_schema_name
ここまで、model の出力先がどのように決まるのか見てきたが、その挙動は実は macro によって制御されている。
dbt には予めgenerate_database_name
という macro とgenerate_schema_name
という marco が用意されている。
そしてこれらの macro はdbt run
などを実行したときに暗黙的に呼び出され、そこに書かれているロジックによって macro の出力先が決まる。
そしてどちらの macro も、自分で定義して既存の振る舞いを上書きすることができる。
generate_database_name
generate_database_name
は、出力先の database を決めるための macro 。project
はdatabase
と互換性があるため、database
はそのままproject
に読み替えてしまって問題ない。
デフォルトの実装は以下のようになっている。
{% macro generate_database_name(custom_database_name=none, node=none) -%} {%- set default_database = target.database -%} {%- if custom_database_name is none -%} {{ default_database }} {%- else -%} {{ custom_database_name | trim }} {%- endif -%} {%- endmacro %}
引数のcustom_database_name
は、 model に設定されたdatabase
(もしくはproject
) config 。
これが設定されない場合はdefault_database
、つまりtarget.database
(BigQuery の場合はtarget.project
)が使われる。
custom_database_name
が設定されている場合は、それがそのまま(trim
だけして)使われる。
このように、この記事でここまで説明してきた挙動は、この macro によって定義されているのである。
generate_schema_name
generate_schema_name
も同様に、出力先の schema を決定するロジックを定義している。dataset
はschema
と互換性があるため、schema
はそのままdataset
に読み替えてしまって問題ない。
デフォルトの実装は以下。
{% macro generate_schema_name(custom_schema_name, node) -%} {%- set default_schema = target.schema -%} {%- if custom_schema_name is none -%} {{ default_schema }} {%- else -%} {{ default_schema }}_{{ custom_schema_name | trim }} {%- endif -%} {%- endmacro %}
独自定義によるオーバーライド
macros/generate_database_name.sql
あるいはmacros/generate_schema_name.sql
を定義することで、挙動を上書きすることができる。
例として、以下の内容でmacros/generate_database_name.sql
を作成して、出力先の database(もしくは project)を決めるロジックを自分で定義してみる。
{% macro generate_database_name(custom_database_name=none, node=none) -%} {{ 'dbt-project-y' }} {%- endmacro %}
このように定義してdbt run
すると、target や config でproject
をどのように設定していても、その設定は使われず、全ての model がdbt-project-y
に出力されるようになる。
source の接続先
project
やdataset
は source にも設定できる。
しかし source に対してはgenerate_database_name
やgenerate_schema_name
が実行されることはなく、project
やdataset
に設定した値がそのまま使われる。
そのため、以下のように設定した状態でfrom {{ source('src', 'user') }}
とすると、dbt-project-b-432615.src.user
が参照される。
version: 2 sources: - name: src # dataset を省略した場合は name が dataset として使われる project: dbt-project-b-432615 tables: - name: user columns: - name: id - name: name
target によって参照先を変えるということも可能。
以下のようにすると、dev
のときはdbt-project-a-432612
を、prod
のときはdbt-project-b-432615
を参照するようになる。
version: 2 sources: - name: src project: | {%- if target.name == "dev" -%} dbt-project-a-432612 {%- elif target.name == "prod" -%} dbt-project-b-432615 {%- else -%} invalid_database {%- endif -%} tables: - name: user columns: - name: id - name: name
そして例えばprofiles.yml
を以下のようにしておくと、--target dev
でdbt run
したときはdbt-project-a-432612.src.user
を参照してdbt-project-x
に model を出力し、--target prod
のときはdbt-project-b-432615.src.user
を参照してdbt-project-y
に model を出力するようになる。
config で model の出力先を設定している場合は、もちろんそれが採用される。
my_profile_1: # profile の名前 target: dev # デフォルトで使う target の名前 outputs: dev: # target の名前 type: bigquery method: oauth project: dbt-project-x dataset: dest prod: # target の名前 type: bigquery method: oauth project: dbt-project-y dataset: dest
参考資料
- About dbt Core data platform connections | dbt Developer Hub
- Connection profiles | dbt Developer Hub
- About target variables | dbt Developer Hub
- dbt_project.yml | dbt Developer Hub
- Configs, properties, what are they? | dbt Developer Hub
- Model configurations | dbt Developer Hub
- Custom databases | dbt Developer Hub
- Custom schemas | dbt Developer Hub
- [CT-260] [Bug]
generate_database_name
does not apply to sources · Issue #4753 · dbt-labs/dbt-core