🐈

dbtの開発を楽にしてくれるパッケージ「codegen」

2023/10/01に公開

前にcodegenを使ったような?使ってなかったような??
記憶が曖昧ですが、真面目に使い方を確認したことが無いので、この機に触ってみたいと思います。

codegen

https://hub.getdbt.com/dbt-labs/codegen/latest/
codegenって何なのよ?
マクロを使ってYAMLファイルやベースモデルのSQLをいい感じに自動作成してくれるみたいです。

packages.ymlの設定

packages.ymlにcodegenを使えるよう設定します。

packages:
  - package: dbt-labs/codegen
    version: 0.11.0

その後、dbt depsコマンドでパッケージをインストールします。

codegenに用意されているマクロたち

generate_source

名前のとおり、sourceのYAMLファイルの中身を生成して出力してくれます。
引数のschema_nameがsourcesのnameとして出力されます。

dbt run-operation generate_source --args '{"schema_name": "PUBLIC", "database_name": "DEMO_DB", "table_names":["CUSTOMERS", "ORDERS"]}'
01:58:05  Running with dbt=1.6.1
01:58:06  Registered adapter: snowflake=1.6.2
01:58:06  Found 5 models, 3 seeds, 8 tests, 0 sources, 0 exposures, 0 metrics, 617 macros, 0 groups, 0 semantic models
01:58:06  version: 2

sources:
  - name: public
    tables:
      - name: customers
      - name: orders

create_base_models

create_base_modelsはベースモデルを作成するスクリプトを生成するマクロです。

dbt run-operation codegen.create_base_models --args '{source_name: public, tables: ["stg_orders"]}'
03:22:27  Running with dbt=1.6.1
03:22:28  Registered adapter: snowflake=1.6.2
03:22:28  Found 5 models, 3 seeds, 8 tests, 3 sources, 0 exposures, 0 metrics, 502 macros, 0 groups, 0 semantic models
03:22:28  Run these commands in your shell to generate the models:
source dbt_packages/codegen/bash_scripts/base_model_creation.sh public raw_orders

base_model_creation

create_base_modelsを実行して生成されたスクリプトを実行します。
source dbt_packages/codegen/bash_scripts/base_model_creation.sh public raw_ordersを実行すると、models配下に「stg_public__raw_orders.sql」が作成され、中身は以下の通りです。


[0m03:23:52  Found 7 models, 3 seeds, 8 tests, 3 sources, 0 exposures, 0 metrics, 502 macros, 0 groups, 0 semantic models
[0m03:23:54

with source as (

    select * from {{ source('public', 'raw_orders') }}

),

renamed as (

    select
        id,
        user_id,
        order_date,
        status

    from source

)

select * from renamed

このSQファイルのままdbt runを実行しても失敗します。
なので、スクリプトを修正してSQLファイルの不要な先頭行を削除します。

#!/bin/bash

echo "" > models/stg_$1__$2.sql

dbt run-operation codegen.generate_base_model --args '{"source_name": "'$1'", "table_name": "'$2'"}' | tail -n +5 >> models/stg_$1__$2.sql

これで再度source dbt_packages/codegen/bash_scripts/base_model_creation.sh public raw_ordersを実行すればOK。

generate_base_model

ベースモデルを生成するマクロです。
コマンド見ればわかりますが、dbt_packages/codegen/bash_scripts/base_model_creation.shと同じコマンドを実行しているだけです。
標準出力するか、ファイルとして保存するかの違いですね。

dbt run-operation generate_base_model --args '{"source_name": "public", "table_name": "raw_orders"}'
03:58:35  Running with dbt=1.6.1
03:58:36  Registered adapter: snowflake=1.6.4
03:58:36  Found 6 models, 3 seeds, 8 tests, 3 sources, 0 exposures, 0 metrics, 502 macros, 0 groups, 0 semantic models
03:58:38

with source as (

    select * from {{ source('public', 'raw_orders') }}

),

renamed as (

    select
        id,
        user_id,
        order_date,
        status

    from source

)

select * from renamed

generate_model_yaml

モデルのYAMLを出力するマクロです。
これ正直osmosisでいい気がする・・・

dbt run-operation generate_model_yaml --args '{"model_name": "raw_customers"}'
02:24:41  Running with dbt=1.6.1
02:24:42  Registered adapter: snowflake=1.6.2
02:24:42  Found 5 models, 3 seeds, 8 tests, 3 sources, 0 exposures, 0 metrics, 617 macros, 0 groups, 0 semantic models
02:24:44  version: 2

models:
  - name: raw_customers
    description: ""
    columns:
      - name: id
        description: ""

      - name: first_name
        description: ""

      - name: last_name
        description: ""

generate_model_import_ctes

これはCTE風にSQLを生成してくれるマクロです。
これの使い方がよく分からなかったです。パッと見CTEで書けていないクエリをCTEE風に書き直したクエリを出力してくれると思ったけど、違った。
jaffle shopのモデルを試しに食わせてみたら、若干違うクエリが出力されました。
最終的なアウトプットのデータ構造は同じでした。
使い所が限られてそうですが、便利なマクロです。

dbt run-operation generate_model_import_ctes --args '{"model_name": "customers"}'
02:34:27  Running with dbt=1.6.1
02:34:27  Registered adapter: snowflake=1.6.2
02:34:28  Unable to do partial parsing because a project config has changed
02:34:30  Found 5 models, 3 seeds, 8 tests, 3 sources, 0 exposures, 0 metrics, 502 macros, 0 groups, 0 semantic models
02:34:30  with stg_customers as (

    select * from {{ ref('stg_customers') }}

),

stg_orders as (

    select * from {{ ref('stg_orders') }}

),

stg_payments as (

    select * from {{ ref('stg_payments') }}

),

customers as (

    select * from stg_customers

),

orders as (

    select * from stg_orders

),

payments as (

    select * from stg_payments

),

customer_orders as (

    select
        customer_id,

        min(order_date) as first_order,
        max(order_date) as most_recent_order,
        count(order_id) as number_of_orders
    from orders

    group by customer_id

),

customer_payments as (

    select
        orders.customer_id,
        sum(amount) as total_amount

    from payments

    left join orders
        on
            payments.order_id = orders.order_id

    group by orders.customer_id

),

final as (

    select
        customers.customer_id,
        customers.first_name,
        customers.last_name,
        customer_orders.first_order,
        customer_orders.most_recent_order,
        customer_orders.number_of_orders,
        customer_payments.total_amount as customer_lifetime_value

    from customers

    left join customer_orders
        on customers.customer_id = customer_orders.customer_id

    left join customer_payments
        on customers.customer_id = customer_payments.customer_id

)

select * from final

Discussion