dbt jinja テンプレートエンジンの基礎構文について調べてみた

2024.06.17

こんにちは!よしななです。
今回は、dbt jinja テンプレートエンジンの基礎構文について実際に自分の手元で触って挙動を確かめたので、備忘録として残します。

目次

  • 前提
  • jinja とは
  • jinja の基礎構文
    • 変数の代入と参照
    • if 構文
    • for 構文
  • まとめ
  • 参考文献

前提

本ブログのコードは以下の環境で実行しています。

  • OS
    • Windows 11
  • ターミナル
    • VSCode / PowerShell で実行

本ブログのコードを実行するには、以下の準備が必要です。
今回は、以下の準備がすべて完了した前提で進めます。

  • 環境セットアップ
  • dbt initコマンドを実行し、dbt モデルを格納するフォルダtest_jinjaを作成

  • データの準備

    • Kaggle - Rossmann Store Sales の train.csv を使用しています
    • Amazon Athena で train.csv をもとに以下の DDL 文を実行しています。
    CREATE EXTERNAL TABLE `test_jinja`(
      `store` int, 
      `dayofweek` int, 
      `date` date, 
      `sales` int, 
      `customers` int, 
      `open` int, 
      `promo` int, 
      `stateholiday` int, 
      `schoolholiday` int)
    ROW FORMAT SERDE 
      'org.apache.hadoop.hive.serde2.OpenCSVSerde' 
    STORED AS INPUTFORMAT 
      'org.apache.hadoop.mapred.TextInputFormat' 
    OUTPUTFORMAT 
      'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat'
    LOCATION
      "{train.csv を格納している S3 バケット名}"
    TBLPROPERTIES (
      'classification'='csv', 
      'columnsOrdered'='false', 
      'compressionType'='none', 
      'delimiter'=',', 
      'skip.header.line.count'='1', 
      'transient_lastDdlTime'='1717037230')
  • dbt init実行で作成したtest_jinjaプロジェクト配下に dbt モデル:test_jinja_01.sqlを作成します。
  • test_jinja_01.sqlに dbt モデルのロジックを書いていきます。

jinja とは

jinja とは、Python で開発されたテンプレートエンジンの一種で、HTML や CSS など様々なテキストファイルの記述に用いられます。
dbtでは、SQL と jinjaを組み合わせることができ、dbt プロジェクト内で SQL の機能を強化・拡張するためのツールとして機能します。
jinja を用いて dbt でできることについて、次項で説明します。

jinja の基礎構文

  • {{ ... }}:式
    • 式の結果を文字列として出力します。
    • 式を使用して変数を参照したり、dbt macros を呼び出したりすることができます。
  • {% ... %}:制御構文
    • for ループや if 文の設定、変数の設定や変更、マクロの定義など、フローの制御に使用します。
  • {# ... #}:コメント
    • dbt モデルとして実行しないコメントなどを挿入する場合に使用します。

変数の代入と参照

Jinja の中で変数を代入する場合は{%- set -%}を使用し、変数を参照する場合は{{ 変数名 }}を入力します。
以下の dbt モデル:test_jinja_01では{%- set -%}my_varを変数として定義し、jinjaを代入しています。
{{ my_var }}部分で変数を参照し、test_jinjaテーブルとしてselect * fromで参照されるようにしています。

{%- set my_var = 'jinja' -%} 
select * from test_{{ my_var }}

Athena での実行結果

dbt モデル:test_jinja_01 が起動し、select * from test_jinjaが実行できました。

2024-06-12_15h10_35

if 構文

jinja の if 構文を使用することにより、{% if "{条件式}" %}部分で指定した条件によって処理を分岐させることができます。
例えば、以下のprofiles.yml内のtarget: prod部分を変更することで、jinja 側でtarget.nameを変更することができます。 profiles.ymlに応じてデータを保存するデータソースを動的に切り替えることが可能です。

profiles.yml

test_jinja:
  outputs:
  target: prod
    prod:
      database: awsdatacatalog
      region_name: ap-northeast-1
      s3_data_dir: "{指定したS3 フォルダ名}"
      s3_staging_dir: "{指定したS3 フォルダ名}"
      schema: "{Athena に保存したデータベース名}"
      threads: 1
      type: athena

profiles.yml内のtarget.nameで指定された変数の値を判定します。
target.name== 'prod'の場合とそうでない場合で処理を分岐させることが可能です。

{% if target.name == 'prod' %}
select
    store
    dayofweek
    date
from test_jinja
{% else %}
select
    sales
    customers
from test_jinja
{% endif %}

Athena での実行結果

profiles.ymltarget.nameprodを指定しているため、
{% if target.name == 'prod' %}~以降のselect句が生成されていることを実行結果から確認できました。

for 構文

jinja の for 構文は、データの繰り返し処理を行う際に使用します。
例えば、以下のようにsetsource_columns変数にテーブルの列名を格納し、Jinja のループ処理を使用して動的にselect句を生成しています。

{% set source_columns = ["store", "dayofweek", "date", "sales", "customers", "open", "promo", "stateholiday", "schoolholiday"] %}

select
    {% for col in source_columns %}
        {{ col }}{% if not loop.last %}, {% endif %}
    {% endfor %}
from
    test_jinja

Athena での実行結果

source_columnsにセットしたカラム名をループ処理して、select句を生成していることが実行結果から確認できました。

2024-06-12_15h11_49

for 構文で使用可能な変数について

loop 変数を使用し、繰り返し処理の制御が可能です。
今回の例ではloop.lastを使用し、source_columnsに入れたリストをすべて繰り返して処理を行っています。

変数名 説明
loop.index 何ループ目か返します(1始まり)
loop.index0 何ループ目か返します(0始まり)
loop.revindex あと何ループか返します(1始まり)
loop.revindex0 あと何ループか返します(0始まり)
loop.first 最初のループのときは true を返します
loop.last ループの最後のときに true を返します
loop.length 何回ループするのか返します

まとめ

jinja の構文については以上となります。
次の記事でdbt 組み込み jinja 関数について手元で実行した結果と調査した挙動について紹介できればと思います。
ここまで読んでいただきありがとうございました!

参考文献