🎉

dbt macro tips advent calendar 2022 day 3 - macroの引数

2022/12/03に公開

便利なデータ変換ツールである dbt の中のmacroに関するtipsを書いていく dbt macro tips Advent Calendar 2022 3日目です。

macroの引数

先日の一番簡単なmacroを再掲します。

macro/noop.sql
{%- macro noop() %}
-- this macro is nothing todo.
{%- endmacro %}

このmacroは固定の内容を描画するのみでした。
しかし、場合によってはmacroの呼び出し側のモデルで、描画内容を指定したい場合もあります。

そのような場合、macroに引数 を定義することで対応できます。

基本形

上記のnoop macroを次のように変更します。

macro/noop.sql
{%- macro noop(msg) %}
-- {{ msg }}
{%- endmacro %}

macroの名前の次のカッコの中に、必要としている引数をカンマ区切りで記述します。
また、与えられた引数の内容はmodel内でよく使う `{{ }}``の記述を使って参照することができます。

試してみましょう。呼び出し側を次のようにします。

diff --git a/models/example/my_first_dbt_model.sql b/models/example/my_first_dbt_model.sql
index ad10f26..270248f 100644
--- a/models/example/my_first_dbt_model.sql
+++ b/models/example/my_first_dbt_model.sql
@@ -8,7 +8,7 @@
 */
 
 {{ config(materialized='table') }}
-{{ noop() }}
+{{ noop('任意のメッセージ') }}
 
 with source_data as (
 
diff --git a/models/example/my_second_dbt_model.sql b/models/example/my_second_dbt_model.sql
index c91f879..ac091b7 100644
--- a/models/example/my_second_dbt_model.sql
+++ b/models/example/my_second_dbt_model.sql
@@ -1,5 +1,6 @@
 
 -- Use the `ref` function to select from other models
+{{ noop('こっちは2つ目') }}
 
 select *
 from {{ ref('my_first_dbt_model') }}

先日と同様にcompileしてみましょう。以下のようになります。

target/compiled/macro_tips_advcal/models/example/my_first_dbt_model.sql
/*
    Welcome to your first dbt model!
    Did you know that you can also configure models directly within SQL files?
    This will override configurations stated in dbt_project.yml

    Try changing "table" to "view" below
*/



-- 任意のメッセージ

with source_data as (

    select 1 as id
    union all
    select null as id

)

select *
from source_data

/*
    Uncomment the line below to remove records with null `id` values
*/

-- where id is not null
target/compiled/macro_tips_advcal/models/example/my_second_dbt_model.sql
-- Use the `ref` function to select from other models

-- こっちは2つ目

select *
from "postgres"."dev"."my_first_dbt_model"
where id = 1

また、引数はカンマ区切りで異なる名前を複数指定できます。

macro/noop.sql
{%- macro noop(msg1, msg2) %}
-- {{ msg1 }} {{ msg2 }}
{%- endmacro %}

必要な個数分だけ定義して、呼び出すときに必要な分与えると良いでしょう。

デフォルト引数

引数は右から順番に省略することが可能です。
以下のように、省略したときのデフォルトの値を指定することで、その引数は呼び出し時に省略することが可能です。
大事な点は、省略できるのは右から順番というところです。

macro/noop.sql
{%- macro noop(msg1='this macro is nothing todo.', msg2='このマクロは何もしないよ!') %}
-- {{ msg1 }} {{ msg2 }}
{%- endmacro %}

この場合、以下のような呼び出しパターンがあります。

models/noop.sql
{{ noop() }}
{{ noop('') }}
{{ noop('','') }}
select 1

compileすると以下のようになります。

target/compiled/macro_tips_advcal/models/noop.sql

-- this macro is nothing todo. このマクロは何もしないよ!

-- このマクロは何もしないよ!

-- 
select 1

可変引数、キーワード引数

場合によっては引数を動的に変えたいケースがあります。その場合に活躍するのは次の2つになります。

  • varargs: 可変引数
  • kwargs: キーワード引数

この2つは特殊で、macro中で使用されていると自動的に有効になります。

例えば、次のように定義した場合は、可変引数を使うことになります。

macro/noop.sql
{%- macro noop(msg) %}
-- {{ msg }} {{ varargs }}
{%- endmacro %}

呼び出し方は次のようになります。

models/noop.sql
{{ noop('this macro is nothing todo.') }}
{{ noop('this macro is nothing todo.','arg1') }}
{{ noop('this macro is nothing todo.','arg1','arg2') }}
select 1

compileの結果は次のようになります。

target/compiled/macro_tips_advcal/models/noop.sql

-- this macro is nothing todo. ()

-- this macro is nothing todo. ('arg1',)

-- this macro is nothing todo. ('arg1', 'arg2')
select 1

varargs は tupleというものになっており、各要素は {{ varargs[0] }} {{ varargs[1] }} というように参照できます。実際の引数の数は `{{ varargs | length }} という表記で参照できます。 この辺のtupleの取り扱い等は、後日取り上げる予定の制御構文や変数に関係してきますので、より詳しい使い方はその時に取り上げます。

もう一つの動的な引数のとり方はキーワード引数となります。先程と同様に以下のようなmacroを定義すると

macro/noop.sql
{%- macro noop(msg) %}
-- {{ msg }} {{ kwargs }}
{%- endmacro %}

次のような呼び出し方が可能になります。

models/noop.sql
{{ noop('this macro is nothing todo.') }}
{{ noop('this macro is nothing todo.',arg1='hoge') }}
{{ noop('this macro is nothing todo.',arg1='hoge', arg2='fuga') }}
{{ noop('this macro is nothing todo.',**{'arg1':'hoge', 'arg2':'fuga'}) }}
select 1

complieした結果は次のようになります

target/compiled/macro_tips_advcal/models/noop.sql

-- this macro is nothing todo. {}

-- this macro is nothing todo. {'arg1': 'hoge'}

-- this macro is nothing todo. {'arg1': 'hoge', 'arg2': 'fuga'}

-- this macro is nothing todo. {'arg1': 'hoge', 'arg2': 'fuga'}
select 1

kwargs はdictというものになっており、各要素は{{ kwargs['arg1'] }} kwargs['arg2'] というように参照できます。 varargsと同様、制御構文や変数とともに使用すると、驚くほど柔軟なmacroを作成できるようになります。柔軟すぎるというのも困ることがあるので使用は用法と用量をお考えの上お使いください。

また、varargskwargs は一緒に使用することもできます。


いきなり、可変引数とキーワード引数の話が出てきてtips感がましてきたでしょう?。
4日目は制御構文について説明します。

Discussion