dbt のモデル とは
dbtにおいてモデルとはSQLのSELECT
文のことです。モデルは.sql
(.py
)ファイルで定義され、デフォルトではmodels
ディレクトリに格納されます。
SQL モデル
各.sql
ファイルは1つのモデルとして1つのSELECT
文で構成されます。また、ファイルの名前がモデル名として使用されます。dbt run
コマンドを実行するとdbtはモデルをCREATE VIEW as
またCREATE TABLE as
のステートメントでラップしてSQLを実行します。
例えば次のcustomers
モデルを考えます。
with customer_orders as (
select
customer_id,
min(order_date) as first_order_date,
max(order_date) as most_recent_order_date,
count(order_id) as number_of_orders
from jaffle_shop.orders
group by 1
)
select
customers.customer_id,
customers.first_name,
customers.last_name,
customer_orders.first_order_date,
customer_orders.most_recent_order_date,
coalesce(customer_orders.number_of_orders, 0) as number_of_orders
from jaffle_shop.customers
left join customer_orders using (customer_id)
dbt run
コマンドを実行すると、dbtは次のSQLを実行してcustomers
というviewテーブルを作成します。このとき、viewテーブルは指定されたスキーマ(データセット)に作成されます。次の例でいうとスキーマ(データセット)はdbt_alice
です。また、viewテーブルの名前であるcustomers
はファイル名のcustomers.sql
から来ています。
create view dbt_alice.customers as (
with customer_orders as (
select
customer_id,
min(order_date) as first_order_date,
max(order_date) as most_recent_order_date,
count(order_id) as number_of_orders
from jaffle_shop.orders
group by 1
)
select
customers.customer_id,
customers.first_name,
customers.last_name,
customer_orders.first_order_date,
customer_orders.most_recent_order_date,
coalesce(customer_orders.number_of_orders, 0) as number_of_orders
from jaffle_shop.customers
left join customer_orders using (customer_id)
)
dbtはモデルをCREATE TABLE as
ステートメントでラップしていることになります。
モデルの設定
モデルはdbt_project.yml
や各モデルのconfig
ブロックで設定することができます。例えばマテリアライゼーションとスキーマを設定したい場合は次のようになります。
name: jaffle_shop
config-version: 2
...
models:
jaffle_shop: # this matches the `name:`` config
+materialized: view # this applies to all models in the current project
marts:
+materialized: table # this applies to all models in the `marts/` directory
marketing:
+schema: marketing # this applies to all models in the `marts/marketing/`` directory
{{ config(
materialized="view",
schema="marketing"
)}}
with customer_orders as ...
設定は階層的に適用されます。サブディレクトリに適用された設定は、上位に設定された条件よりも優先されます。例えばあるモデルについてdbt_project.yml
とconfig
の両方で設定されている場合はconfig
の設定値が優先されます。
Python モデル
Pythonモデルでは、データサイエンスや統計学の最先端パッケージを含むPythonエコシステムで利用できるツールを使うことができます。
各Pythonモデルはmodels/
フォルダにある.py
ファイルに格納されます。model()
というの関数が定義され、次の2つのパラメータを受け取ります。
- dbt
dbt Coreによってコンパイルされた各モデルに固有のクラスで、モデルの依存関係を表現することができます。 - session
データプラットフォームからPythonバックエンドへの接続を表すクラスです。session
はテーブルをDataFrameとして読み込んだり、DataFrameをテーブルに書き戻したりするために必要とされます。
model()
関数は、1つのDataFrameを返す必要があります。
import ...
def model(dbt, session):
final_df = ... # stuff you can't write in SQL!
return final_df
モデル関数の定義に加えて、他の関数をインポートしたり独自の関数を定義したりすることもできます。
def add_one(x):
return x + 1
def model(dbt, session):
dbt.config(materialized="table")
temps_df = dbt.ref("temperatures")
# warm things up just a little
df = temps_df.withColumn("degree_plus_one", add_one(temps_df["degree"]))
return df
import holidays
def is_holiday(date_col):
# Chez Jaffle
french_holidays = holidays.France()
is_holiday = (date_col in french_holidays)
return is_holiday
def model(dbt, session):
dbt.config(
materialized = "table",
packages = ["holidays"]
)
orders_df = dbt.ref("stg_orders")
df = orders_df.to_pandas()
# apply our function
# (columns need to be in uppercase on Snowpark)
df["IS_HOLIDAY"] = df["ORDER_DATE"].apply(is_holiday)
# return final dataset (Pandas DataFrame)
return df
モデルの設定
モデルは次の3パターンで設定することができます。
dbt_project.yml
で設定するmodels/
ディレクトリに.yml
ファイルを作成して設定する- 各モデル
.py
内のdbt.config()
メソッドを呼び出して設定する
models/
ディレクトリの.yml
ファイルを作成して設定する場合は次のようになります。
version: 2
models:
- name: my_python_model
config:
materialized: table
target_name: "{{ target.name }}"
specific_var: "{{ var('SPECIFIC_VAR') }}"
specific_env_var: "{{ env_var('SPECIFIC_ENV_VAR') }}"
def model(dbt, session):
target_name = dbt.config.get("target_name")
specific_var = dbt.config.get("specific_var")
specific_env_var = dbt.config.get("specific_env_var")
orders_df = dbt.ref("fct_orders")
# limit data in dev
if target_name == "dev":
orders_df = orders_df.limit(500)
.py
内のdbt.config()
メソッドを呼び出して設定する場合は次のようになります。
def model(dbt, session):
# setting configuration
dbt.config(materialized="table")
Limitations
Pythonモデルは次の欠点があります。
- 時間とコスト
PythonモデルはSQLモデルよりも実行速度が遅く、それを実行するクラウドリソースも高価になります。Pythonの実行には、より汎用的なコンピューティングが必要であり、また、SQLモデルとは別のサービスやアーキテクチャが必要な場合もあります。 - Syntaxの違い
dbtはディスパッチパターンやdbt_utils
などのパッケージを通じて、一般的なデータウェアハウスにおけるSQL方言の違いの抽象化に努めてきました。PythonはSQLよりも遥かに多くの方言が存在します。SQLで何かをする方法が5つあるとしたら、Pythonでそれを書く方法は500通りあり、性能も標準への準拠も様々です。
SQLでもPythonでも同じように書ける変換がある場合はSQLの方が良さそうです。SQLで書けない変換がある、あるいは10行のエレガントで注釈付きのPythonで、読みにくいJinja-SQLの1000行を節約できる場合であればPythonを選択するのが良さそうです。
参考