【日本語解説】dbtスタイルガイド:読みやすいデータモデルの書き方

こんにちは、六本木アナリティクスエンジニアのTaku(@aelabdata )です。

dbtを使ったデータモデリングを効率的に進めるには、プロジェクトの構造だけでなく、SQLやJinja、YAMLファイルの「書き方」 を統一することが非常に重要です。

チームメンバーが増え、プロジェクトが大規模になるにつれて、「あの人が書いたコード、どこをどう読めばいいか分からない…」「修正箇所を探すのに時間がかかる…」といった問題に直面することがあります。これは、個々人のコーディングスタイルがバラバラなために起こる現象です。

dbtは、ソフトウェア開発のベストプラクティスをデータの世界に持ち込みます。そして、ソフトウェア開発において、コードのスタイル統一は、可読性の向上、保守性の確保、そしてチーム開発を円滑に進めるための基本中の基本です。

今回は、dbtの公式ドキュメント「How we style our dbt projects」を参考に、dbtプロジェクトのコーディングスタイルを統一するための主要なポイントを解説します。

公式スタイルガイドが目指すもの

公式ドキュメントが推奨するスタイルガイドの目的は、以下の3つです。

  • 一貫性(Consistency): プロジェクト全体で同じルールに従って記述することで、誰が書いても同じスタイルに
  • 可読性(Readability): 誰もが簡単にコードを読めるように
  • シンプルさ(Simplicity): 不必要な複雑さを排除し、本質的なロジックに集中できるように

これらの原則を念頭に、具体的なスタイリングのルールを見ていきましょう。

SQLのスタイルガイド:読みやすいSQLの書き方

dbtプロジェクトの心臓部であるSQLは、特にスタイルの統一が重要です。

SQL基本

  • 自動整形ツール:
    • SQLFluffSQLFluffを使ってスタイルルールを自動で維持しましょう。dbt_project.ymlのように、.sqlfluffファイルでルールをカスタマイズできます。
    • 除外設定target/dbt_packages/macros/など、不要なフォルダやファイルを.sqlfluffignoreファイルで除外すると、リンティングが高速化されます。
  • コメント:
    • Jinjaコメント{# ... #} を使うと、コンパイルされたSQLにはコメントが含まれません。
  • カンマ:
    • SELECT文の各フィールドの後にカンマを付け、最後のフィールドにはカンマを付けません。
  • インデント:
    • インデントはスペース4つ分を使用します。
  • 文字種:
    • フィールド名、キーワード、関数名は全て小文字にしましょう。selectfromorder_idなど。
  • エイリアス:
    • フィールドやテーブルにエイリアスを付ける際は、asキーワードを明示的に使用します。

フィールド、集計、グルーピング

  • フィールドの順番:
    • SELECT文では、集計関数やウィンドウ関数よりも前に、フィールド名を記述します。
  • 集計の早期実行:
    • パフォーマンス最適化のため、集計処理は可能な限り早い段階(結合する前に)実行しましょう。これにより、結合対象のデータセットが小さくなり、処理が高速化されます。
  • GROUP BYの指定:
    • GROUP BY 1, 2, ...のように番号で指定する方法が、カラム名を記述するよりも推奨されます。これにより、SELECT文のカラムの順番が変わっても、GROUP BY句を変更する必要がなくなります。ただし、グループ化するカラムが多すぎる場合は、モデル設計を見直す良い機会かもしれません。

JOINの書き方

JOINはSQLの可読性を大きく左右します。以下のルールを意識しましょう。

  • UNIONUNION ALL:
    • 重複を明示的に削除したい場合を除き、UNIONの代わりにUNION ALLを使用しましょう。UNION ALLUNIONよりも処理が高速です。
  • カラム名のプレフィックス:
    • 2つ以上のテーブルを結合する場合、必ずカラム名にテーブル名をプレフィックスとして付けましょう。これにより、どのカラムがどのテーブル由来か一目で分かります。
  • JOINタイプの明記:
    • JOINとだけ書くのではなく、INNER JOINLEFT JOINなど、結合タイプを明示的に書きましょう。
  • テーブルのエイリアス:
    • FROM句やJOIN句では、customerscのように短縮したエイリアスは避けましょう。customersのようにフルネームを使う方が、コードの可読性が高まります。
  • JOINの方向:
    • JOIN左から右へ順に進めましょう。RIGHT JOINは、FROM句とJOIN句のテーブルを入れ替えることでLEFT JOINに書き換えることができ、ロジックが理解しやすくなります。

CTEのスタイルガイド

CTEを適切に使うことで、SQLの可読性と再利用性は飛躍的に向上します。

『Import』CTE

  • 配置場所全ての{{ ref(...) }}文を、ファイルの冒頭のCTEにまとめます。これにより、このモデルがどのソースに依存しているか一目で分かります。
  • 命名規則: 参照するテーブル名と同じ名前を付けます。
  • データスキャン: 性能向上のため、できる限りデータスキャンを制限します。使用するカラムのみを選択し、WHERE句で不要なデータをフィルタリングしましょう。

『Functional』CTE

  • 目的: パフォーマンスが許す限り、CTEは単一の論理的な作業を実行するようにします。
  • 命名規則: CTEの名前は、その機能が伝わるよう、できるだけ詳細に記述します。
  • 重複の排除: 複数のモデルで重複するロジックは、中間モデルとして独立させ、再利用できるようにしましょう。
  • 最終出力: モデルの最後の行は、最終的なCTEをSELECT *で選択するようにしましょう。これにより、開発中に各ステップの出力を簡単に確認できます。

モデル設定のスタイル

モデル固有の設定は、dbt_project.ymlmodelファイルのconfigブロックで管理できます。

  • dbt_project.yml: 特定のディレクトリ内の全てのモデルに適用される設定は、dbt_project.ymlに記述します。
  • モデル固有の設定: 特定のモデルにのみ適用される設定は、モデルファイルのconfigブロックに記述します。可読性を最大化するために、以下のように記述することが推奨されます。
{{ config(
    materialized = 'incremental',
    unique_key = 'order_id'
) }}

良い例

with orders as (
    select
        order_id,
        customer_id,
        order_date,
        status
    from {{ ref('stg_orders') }}
),

final as (
    select
        order_id,
        customer_id,
        -- Calculate the revenue based on order status
        case when status = 'completed' then 100 else 0 end as revenue
    from orders
    where order_date >= '2023-01-01'
)

select * from final

Jinjaのスタイルガイド:テンプレートを読みやすくする

dbtでは、SQLの中にPythonのようなテンプレート言語であるJinjaを埋め込みます。Jinjaも同様にスタイルを統一することが重要です。

  • Jinjaの区切り:
    • {{ ... }} と {% ... %} の内側と外側にスペースを入れます。
    • 例: {{ ref('my_model') }}
  • ブロックと空白:
    • {% for ... %}{% if ... %}といったブロックは、読みやすさのために適切なインデントをつけます。
    • setdoなどの単一のブロックでは、1行にまとめることも可能です。
  • マクロの呼び出し:
    • マクロの引数は、カンマとスペースで区切ります。
    • 例: {{ dbt_utils.surrogate_key(['customer_id', 'order_id']) }}

YAMLのスタイルガイド:一貫性のある定義ファイル

schema.ymlなどのYAMLファイルは、ドキュメンテーションとテストの基盤となります。

  • インデントは2スペース: YAMLファイルのインデントは、スペース2つ分を使用しましょう。
  • リストアイテムのインデント: リスト(-で始まる項目)は、親要素から適切にインデントします。
  • リストの明示的な記述: 項目が1つのリストでも、文字列ではなく明示的なリストとして記述することが推奨されます。
    • 良い例select: ['other_user']
    • 避けるべき例select: 'other_user'
  • 行の区切り: 辞書(キーと値のペア)を含むリストでは、各項目を新しい行で区切ることで可読性を高めます。
  • 行の長さ: 1行の文字数は80文字以下に抑えましょう。
  • 自動整形ツールの活用:
    • dbt JSONスキーマ: dbtのJSONスキーマと互換性のあるStudio IDEを使用すると、YAMLファイルの検証と自動フォーマットが可能です。
    • Prettier: フォーマッターとしてPrettierの使用が推奨されています。これらのツールを導入することで、手動でスタイルを整える手間が省けます。

良い例:

version: 2
models:
  - name: my_model
    description: "This is a good model."
    columns:
      - name: customer_id
        description: "The unique ID of the customer."
        tests:
          - not_null
          - unique
      - name: order_count
        description: "The total number of orders for the customer."
        tests:
          - not_null
          - dbt_utils.not_null_where:
              condition: "order_count > 0"

まとめ:スタイルガイドをチームの共通言語に

dbtプロジェクトのスタイルガイドを導入することは、単なる見た目の問題ではありません。それは、チームの生産性、保守性、そしてコミュニケーションの質に直結します。

今回ご紹介したルールは、dbtの公式ドキュメントで推奨されているものであり、多くのdbtコミュニティで採用されている「共通言語」と言えます。

  • SQL: 大文字・小文字、インデント、CTEでロジックを整理
  • Jinja: 適切なスペースとインデントで可読性を確保
  • YAML: 統一されたインデントとコメントで定義を明確化

これらのスタイルガイドをチームで共有し、プロジェクトの「共通言語」とすることで、誰もがスムーズに開発に参加できる環境を構築できます。

この記事が役に立ったと感じたら、ぜひX(@aelabdata)をフォローください!日々のアナリティクスエンジニアとしての学びや、記事の更新情報を発信しています。