【dbt】macros

dbtのmacrosフォルダの構成
を考えるメモ
基本的なフォルダ構成
dbt_project/
│── macros/
│ ├── staging/
│ │ ├── get_source.sql
│ │ ├── clean_column_name.sql
│ │ └── generate_schema_name.sql
│ ├── transformations/
│ │ ├── calculate_metrics.sql
│ │ ├── format_date.sql
│ │ └── pivot_data.sql
│ ├── utilities/
│ │ ├── logging.sql
│ │ ├── string_helpers.sql
│ │ └── type_casting.sql
│ ├── constraints/
│ │ ├── check_null_values.sql
│ │ ├── enforce_unique_keys.sql
│ │ └── validate_referential_integrity.sql
│ ├── tests/
│ │ ├── assert_non_null.sql
│ │ ├── assert_unique.sql
│ │ └── assert_positive.sql
│ ├── jinja_helpers.sql
│ ├── dbt_project_utils.sql
│ └── README.md
フォルダの役割
-
staging/
- ソーステーブルのクリーンアップやスキーマ名の自動生成など、データの準備に関連するマクロを格納
-
transformations/
- 集計やデータ変換(ピボット、フォーマット変更など)を行うマクロを格納
-
utilities/
- 文字列操作や型変換などの汎用的なヘルパーマクロを格納
-
constraints/
- データ品質チェックのためのマクロを格納(ユニークキーの検証、NULLチェックなど)
-
tests/
- カスタムテスト(
assert_non_null
など)を定義するマクロを格納
- カスタムテスト(
-
jinja_helpers.sql
- Jinjaの補助的な関数を定義(例えば、リスト操作や条件分岐)
-
dbt_project_utils.sql
- プロジェクト全体で利用するユーティリティマクロを格納
-
README.md
- マクロの使い方や管理ルールを記載

import requests
def get_github_tree(repo, path, indent=0, file=None):
""" 指定した GitHub リポジトリ内のフォルダ以下のツリー構造を取得し、ファイルに出力 """
url = f"https://api.github.com/repos/{repo}/contents/{path}"
response = requests.get(url)
if response.status_code == 200:
items = response.json()
for item in items:
line = " " * indent + "|-- " + item["name"] # ツリー形式の文字列
print(line)
file.write(line + "\n") # ファイルに書き込み
if item["type"] == "dir": # フォルダなら再帰的に取得
get_github_tree(repo, item["path"], indent + 2, file)
else:
print(f"Failed to fetch {path}: {response.status_code}")
# リポジトリとディレクトリの指定
repo = "calogica/dbt-expectations"
path = "macros"
# ツリー構造を取得してファイルに保存
with open("tree.txt", "w", encoding="utf-8") as f:
f.write(f"{repo}/{path}/\n")
get_github_tree(repo, path, file=f)
print("ツリー構造を tree.txt に保存しました。")

calogica/dbt-expectations/macros/
|-- .gitkeep
|-- math
|-- log_natural.sql
|-- median.sql
|-- percentile_cont.sql
|-- rand.sql
|-- regex
|-- regexp_instr.sql
|-- schema_tests
|-- _generalized
|-- _ignore_row_if_expression.sql
|-- _truth_expression.sql
|-- equal_expression.sql
|-- expression_between.sql
|-- expression_is_true.sql
|-- aggregate_functions
|-- expect_column_distinct_count_to_be_greater_than.sql
|-- expect_column_distinct_count_to_be_less_than.sql
|-- expect_column_distinct_count_to_equal.sql
|-- expect_column_distinct_count_to_equal_other_table.sql
|-- expect_column_distinct_values_to_be_in_set.sql
|-- expect_column_distinct_values_to_contain_set.sql
|-- expect_column_distinct_values_to_equal_set.sql
|-- expect_column_max_to_be_between.sql
|-- expect_column_mean_to_be_between.sql
|-- expect_column_median_to_be_between.sql
|-- expect_column_min_to_be_between.sql
|-- expect_column_most_common_value_to_be_in_set.sql
|-- expect_column_proportion_of_unique_values_to_be_between.sql
|-- expect_column_quantile_values_to_be_between.sql
|-- expect_column_stdev_to_be_between.sql
|-- expect_column_sum_to_be_between.sql
|-- expect_column_unique_value_count_to_be_between.sql
|-- column_values_basic
|-- expect_column_values_to_be_between.sql
|-- expect_column_values_to_be_decreasing.sql
|-- expect_column_values_to_be_in_set.sql
|-- expect_column_values_to_be_in_type_list.sql
|-- expect_column_values_to_be_increasing.sql
|-- expect_column_values_to_be_null.sql
|-- expect_column_values_to_be_of_type.sql
|-- expect_column_values_to_be_unique.sql
|-- expect_column_values_to_have_consistent_casing.sql
|-- expect_column_values_to_not_be_in_set.sql
|-- expect_column_values_to_not_be_null.sql
|-- distributional
|-- expect_column_values_to_be_within_n_moving_stdevs.sql
|-- expect_column_values_to_be_within_n_stdevs.sql
|-- expect_row_values_to_have_data_for_every_n_datepart.sql
|-- multi-column
|-- expect_column_pair_values_A_to_be_greater_than_B.sql
|-- expect_column_pair_values_to_be_equal.sql
|-- expect_column_pair_values_to_be_in_set.sql
|-- expect_compound_columns_to_be_unique.sql
|-- expect_multicolumn_sum_to_equal.sql
|-- expect_select_column_values_to_be_unique_within_record.sql
|-- string_matching
|-- _get_like_pattern_expression.sql
|-- expect_column_value_lengths_to_be_between.sql
|-- expect_column_value_lengths_to_equal.sql
|-- expect_column_values_to_match_like_pattern.sql
|-- expect_column_values_to_match_like_pattern_list.sql
|-- expect_column_values_to_match_regex.sql
|-- expect_column_values_to_match_regex_list.sql
|-- expect_column_values_to_not_match_like_pattern.sql
|-- expect_column_values_to_not_match_like_pattern_list.sql
|-- expect_column_values_to_not_match_regex.sql
|-- expect_column_values_to_not_match_regex_list.sql
|-- table_shape
|-- _get_column_list.sql
|-- _list_intersect.sql
|-- expect_column_to_exist.sql
|-- expect_grouped_row_values_to_have_recent_data.sql
|-- expect_row_values_to_have_recent_data.sql
|-- expect_table_aggregation_to_equal_other_table.sql
|-- expect_table_column_count_to_be_between.sql
|-- expect_table_column_count_to_equal.sql
|-- expect_table_column_count_to_equal_other_table.sql
|-- expect_table_columns_to_contain_set.sql
|-- expect_table_columns_to_match_ordered_list.sql
|-- expect_table_columns_to_match_set.sql
|-- expect_table_columns_to_not_contain_set.sql
|-- expect_table_row_count_to_be_between.sql
|-- expect_table_row_count_to_equal.sql
|-- expect_table_row_count_to_equal_other_table.sql
|-- expect_table_row_count_to_equal_other_table_times_factor.sql
|-- utils
|-- datatypes.sql
|-- groupby.sql
|-- md5.sql
このフォルダ構成の特徴
-
math/
- 数学的な計算(
median.sql
,percentile_cont.sql
など) -
log_natural.sql
やrand.sql
など、統計解析向けのマクロも含まれる
- 数学的な計算(
-
regex/
-
regexp_instr.sql
など、文字列パターンマッチングのユーティリティがある
-
-
schema_tests/
-
_generalized/
- 内部で再利用される一般的なロジック(
_truth_expression.sql
など)
- 内部で再利用される一般的なロジック(
-
aggregate_functions/
- 集計値の期待値テスト(
expect_column_sum_to_be_between.sql
など)
- 集計値の期待値テスト(
-
column_values_basic/
- カラムごとの基本的な検証(
expect_column_values_to_be_between.sql
など)
- カラムごとの基本的な検証(
-
distributional/
- データの分布に関するテスト(
expect_column_values_to_be_within_n_stdevs.sql
など)
- データの分布に関するテスト(
-
multi-column/
- 複数カラム間の関係をチェック(
expect_column_pair_values_to_be_equal.sql
など)
- 複数カラム間の関係をチェック(
-
string_matching/
- 正規表現やLIKE演算子を使ったテスト(
expect_column_values_to_match_regex.sql
など)
- 正規表現やLIKE演算子を使ったテスト(
-
table_shape/
- テーブル全体の構造をチェック(
expect_table_column_count_to_be_between.sql
など)
- テーブル全体の構造をチェック(
-
-
utils/
-
md5.sql
やdatatypes.sql
など、基本的なデータ型変換やハッシュ関数を定義 -
groupby.sql
など、汎用的な SQL 操作を補助するマクロを格納
-
この構成のメリット
✅ 汎用性が高い
-
math/
やregex/
で、データ処理に幅広く使えるマクロを管理 -
utils/
で、共通的な便利マクロを提供
✅ データ品質チェック (schema_tests/
) を細かく管理
-
aggregate_functions/
やtable_shape/
など、テストの種類ごとにフォルダを分けている -
_generalized/
で共通ロジックをまとめ、再利用性を高めている
✅ スケールしやすい
- 必要に応じて
schema_tests/
のサブカテゴリを増やせる -
math/
やutils/
など、個別の計算・変換処理をどんどん追加できる

brooklyn-data/dbt_artifacts/macros/
|-- _macros.yml
|-- database_specific_helpers
|-- column_identifier.sql
|-- generate_surrogate_key.sql
|-- get_relation.sql
|-- parse_json.sql
|-- type_helpers.sql
|-- migration
|-- migrate_from_v0_to_v1.sql
|-- upload_individual_datasets
|-- upload_exposures.sql
|-- upload_invocations.sql
|-- upload_model_executions.sql
|-- upload_models.sql
|-- upload_seed_executions.sql
|-- upload_seeds.sql
|-- upload_snapshot_executions.sql
|-- upload_snapshots.sql
|-- upload_sources.sql
|-- upload_test_executions.sql
|-- upload_tests.sql
|-- upload_results
|-- get_column_name_lists.sql
|-- get_dataset_content.sql
|-- get_table_content_values.sql
|-- insert_into_metadata_table.sql
|-- upload_results.sql
このフォルダ構成の特徴
-
database_specific_helpers/
-
column_identifier.sql
,generate_surrogate_key.sql
など、データベースごとの互換性や識別キー生成を補助 -
get_relation.sql
やparse_json.sql
など、リレーション管理や JSON データの処理
-
-
migration/
-
migrate_from_v0_to_v1.sql
という特定バージョンの移行スクリプトを管理
-
-
upload_individual_datasets/
-
upload_exposures.sql
,upload_models.sql
,upload_tests.sql
など、dbt の各種アーティファクト(exposures, models, tests など)のアップロード処理 -
upload_seed_executions.sql
やupload_snapshots.sql
など、実行履歴の管理 も含まれる
-
-
upload_results/
-
get_column_name_lists.sql
,get_dataset_content.sql
など、テーブルやデータセットの情報を取得 -
insert_into_metadata_table.sql
でメタデータを一括登録 -
upload_results.sql
で dbt の実行結果をまとめてアップロード
-
この構成のメリット
✅ dbt のアーティファクト管理に特化
-
upload_individual_datasets/
やupload_results/
で dbt のメタデータ(モデル、スナップショット、テストなど)を効率的に整理 -
database_specific_helpers/
でデータベースごとの処理を統一
✅ メタデータの移行 (migration/
) も考慮
-
migrate_from_v0_to_v1.sql
で、スキーマのバージョン変更や互換性の維持をサポート
✅ データベース依存を考慮
-
database_specific_helpers/
内でtype_helpers.sql
などを定義し、データ型や識別子の処理を標準化 -
parse_json.sql
でデータベースに依存しない JSON 処理を提供

dbt-labs/dbt-codegen/macros/
|-- create_base_models.sql
|-- generate_base_model.sql
|-- generate_model_import_ctes.sql
|-- generate_model_yaml.sql
|-- generate_source.sql
|-- helpers
|-- helpers.sql
|-- vendored
|-- dbt_core
|-- format_column.sql
フォルダ構成の特徴
database_specific_helpers/
- column_identifier.sql, generate_surrogate_key.sql: データベースごとの互換性や識別キー生成を補助するためのSQL。異なるデータベースに対応するロジックを統一。
- get_relation.sql, parse_json.sql: リレーション管理やJSONデータの処理をサポート。データベースに依存しない形で共通の処理を提供。
migration/
- migrate_from_v0_to_v1.sql: 特定のバージョン間での移行を管理するスクリプト。スキーマのバージョン変更や互換性の維持を支援。
upload_individual_datasets/
- upload_exposures.sql, upload_models.sql, upload_tests.sql: dbtの各種アーティファクト(exposures, models, testsなど)のアップロード処理を担当。
- upload_seed_executions.sql, upload_snapshots.sql: 実行履歴の管理を含み、データベース内の状態を追跡。
upload_results/
- get_column_name_lists.sql, get_dataset_content.sql: テーブルやデータセットの情報を取得するためのSQL。dbtのメタデータを整理し、参照する。
- insert_into_metadata_table.sql: メタデータを一括登録するためのSQL。テーブルに対する情報を効率的にまとめてアップロード。
- upload_results.sql: dbtの実行結果をまとめてアップロードするためのメインSQL。
この構成のメリット
✅ dbtのアーティファクト管理に特化
- upload_individual_datasets/ や upload_results/ フォルダを使って、dbtのメタデータ(モデル、スナップショット、テストなど)を効率的に整理できる。
- データベース内のdbtのオブジェクト(テーブル、ビュー、スナップショット)の一貫した管理を行える。
✅ メタデータの移行 (migration/) も考慮
- migrate_from_v0_to_v1.sql で、スキーマのバージョン変更や互換性を保ちながら、移行をサポート。プロジェクトが進化する際にスムーズに変更を加えられる。
✅ データベース依存を考慮
- database_specific_helpers/ 内で、データ型や識別子の処理を標準化するため、異なるデータベース環境においても共通の処理が提供される。
- parse_json.sql のような汎用的な処理を提供することで、データベースの種類に依存しない形でJSONデータを扱える。

dbt-labs/dbt-external-tables/macros/
|-- common
|-- create_external_schema.sql
|-- create_external_table.sql
|-- get_external_build_plan.sql
|-- helpers
|-- dropif.sql
|-- transaction.sql
|-- refresh_external_table.sql
|-- stage_external_sources.sql
|-- update_external_table_columns.sql
|-- plugins
|-- bigquery
|-- create_external_schema.sql
|-- create_external_table.sql
|-- get_external_build_plan.sql
|-- update_external_table_columns.sql
|-- fabric
|-- create_external_schema.sql
|-- create_external_table.sql
|-- get_external_build_plan.sql
|-- helpers
|-- dropif.sql
|-- redshift
|-- create_external_table.sql
|-- get_external_build_plan.sql
|-- helpers
|-- add_partitions.sql
|-- dropif.sql
|-- is_ext_tbl.sql
|-- paths.sql
|-- render_macro.sql
|-- transaction.sql
|-- refresh_external_table.sql
|-- snowflake
|-- create_external_schema.sql
|-- create_external_table.sql
|-- get_external_build_plan.sql
|-- helpers
|-- is_csv.sql
|-- refresh_external_table.sql
|-- snowpipe
|-- create_empty_table.sql
|-- create_snowpipe.sql
|-- get_copy_sql.sql
|-- refresh_snowpipe.sql
|-- spark
|-- create_external_table.sql
|-- get_external_build_plan.sql
|-- helpers
|-- dropif.sql
|-- recover_partitions.sql
|-- refresh_external_table.sql
フォルダ構成の特徴
common/ (共通処理)
-
create_external_schema.sql: 外部スキーマを作成するためのSQL。
-
create_external_table.sql: 外部テーブルを作成するためのSQL。
-
get_external_build_plan.sql: 外部テーブルのビルドプランを取得するSQL。
-
helpers/
:
- dropif.sql: 外部テーブルやスキーマが存在する場合に削除するヘルパーマクロ。
- transaction.sql: トランザクション関連のヘルパーマクロ。外部テーブルの操作をトランザクション内で安全に実行するために使用。
-
refresh_external_table.sql: 外部テーブルのリフレッシュ処理。
-
stage_external_sources.sql: 外部ソースデータをステージングするためのSQL。
-
update_external_table_columns.sql: 外部テーブルのカラム更新処理。
plugins/ (データベース固有のプラグイン)
データベースごとに異なる処理を管理しています。
-
bigquery/
- BigQuery用の外部テーブルに関連するSQLマクロ。
- create_external_schema.sql: BigQuery用の外部スキーマ作成。
- create_external_table.sql: BigQuery用の外部テーブル作成。
- get_external_build_plan.sql: BigQuery用のビルドプラン取得。
- update_external_table_columns.sql: BigQuery用のカラム更新。
-
fabric/
-
Fabric用の外部テーブル関連SQL。
-
create_external_schema.sql, create_external_table.sql: Fabric用の外部スキーマ・テーブル作成。
-
get_external_build_plan.sql: Fabric用のビルドプラン取得。
-
helpers/
:
- dropif.sql: Fabric用の削除処理。
-
-
redshift/
-
Redshift用の外部テーブル関連SQL。
-
create_external_table.sql: Redshift用の外部テーブル作成。
-
get_external_build_plan.sql: Redshift用のビルドプラン取得。
-
helpers/
:
- add_partitions.sql: パーティションの追加を補助するSQL。
- dropif.sql: Redshift用の削除処理。
- is_ext_tbl.sql: 外部テーブルかどうかを確認するSQL。
- paths.sql: パスに関連する処理を補助。
- render_macro.sql: Redshift用のマクロをレンダリング。
- transaction.sql: トランザクション関連の処理。
-
refresh_external_table.sql: 外部テーブルのリフレッシュ。
-
-
snowflake/
-
Snowflake用の外部テーブル関連SQL。
-
create_external_schema.sql, create_external_table.sql: Snowflake用の外部スキーマ・テーブル作成。
-
get_external_build_plan.sql: Snowflake用のビルドプラン取得。
-
helpers/
:
- is_csv.sql: CSVデータの識別に使用。
-
refresh_external_table.sql: 外部テーブルのリフレッシュ。
-
snowpipe/
:
- create_empty_table.sql: 空のテーブルを作成。
- create_snowpipe.sql: Snowpipeを作成するSQL。
- get_copy_sql.sql: Snowflakeにおけるコピー操作のSQL。
- refresh_snowpipe.sql: Snowpipeのリフレッシュ。
-
-
spark/
-
Spark用の外部テーブル関連SQL。
-
create_external_table.sql: Spark用の外部テーブル作成。
-
get_external_build_plan.sql: Spark用のビルドプラン取得。
-
helpers/
:
- dropif.sql: Spark用の削除処理。
- recover_partitions.sql: パーティションの回復処理。
-
refresh_external_table.sql: 外部テーブルのリフレッシュ。
-
この構成のメリット
✅ データベース間での外部テーブル操作の統一
- common/ フォルダ内のマクロは、複数のデータベースに共通する操作を提供し、外部テーブルの作成や更新、リフレッシュの処理を統一的に行うことができます。
✅ データベース固有の拡張
- 各データベースの plugins/ フォルダに特化した処理が定義されており、BigQuery、Redshift、Snowflake、Fabric、Sparkなど、異なるデータベースに合わせた最適な操作が提供されます。
✅ 外部テーブルの効率的な管理
- 外部テーブルを作成する、更新する、リフレッシュする、または削除するための便利なマクロが用意されており、外部テーブルの管理が効率的に行えます。
✅ スキーマやテーブル作成の標準化
- 各データベースで共通のスキーマやテーブル作成のマクロが使えるため、異なるデータベース間での設定変更や作業が統一された方法で実行できます。

dbt マクロのベストプラクティス
1. マクロはシンプルに保つ
各マクロは単一の明確なタスクを実行するように設計する。
一つのマクロに過度なロジックを詰め込むのではなく、小さく分割し、再利用しやすい形にする。
2. 分かりやすい命名規則を使用する
マクロの名前は、その役割が直感的に分かるようにする。
これにより、モデル内で使用した際に可読性が向上し、メンテナンスが容易になる。
3. 例外ケースを考慮する
マクロ内で NULL
値や予期しない入力を考慮し、どのようなデータにも対応できるようにする。
信頼性の高い処理を実装することで、エラーの発生を防ぐ。
4. テストでマクロを活用する
マクロを dbt のテスト内でも使用することで、再利用可能なテストロジックを構築し、
プロジェクト全体のデータ検証の一貫性を保つ。
5. マクロをドキュメント化する
マクロの目的、パラメータ、使用方法についてコメントやドキュメントを追加する。
特に複数のチームメンバーが関与するプロジェクトでは、ドキュメントが理解の助けとなる。

macros.mdとmacros.ymlを生成するスクリプト
参考
version: 2
macros:
- name: <macro name>
description: <markdown_string>
docs:
show: true | false
meta: {<dictionary>}
arguments:
- name: <arg name>
type: <string>
description: <markdown_string>
- ... # declare properties of additional arguments
- name: ... # declare properties of additional macros
version: 2
macros:
- name: resume_warehouse
description: '{{ doc("alter_warehouse") }}'
- name: suspend_warehouse
description: '{{ doc("alter_warehouse") }}'
- name: backup_to_gcs
description: '{{ doc("backup_to_gcs") }}'
- name: get_backup_table_command
description: '{{ doc("get_backup_table_command") }}'
- name: grant_usage_to_schemas
description: '{{ doc("grant_usage_to_schemas") }}'
- name: gdpr_delete
description: '{{ doc("gdpr_delete") }}'
arguments:
- name: email_sha
type: string
description: SHA256 of the email to be removed/redacted
- name: run_queries
type: boolean
description: Flag used to run queries or just print them. Default is False which will only print queries to stdout.
- name: gdpr_delete_gitlab_dotcom
description: '{{ doc("gdpr_delete_gitlab_dotcom") }}'
arguments:
- name: email_sha
type: string
description: SHA256 of the email to be removed/redacted
- name: run_queries
type: boolean
description: Flag used to run queries or just print them. Default is False which will only print queries to stdout.
- name: gdpr_bulk_delete
description: '{{ doc("gdpr_bulk_delete") }}'

import os
import re
import yaml
import json
def main():
"""
メイン関数。指定されたディレクトリのファイルを処理し、macros.md と macros.yml を更新する。
"""
file_path = "~"
file_list = get_files(file_path) # 対象のフォルダを指定
json_formatted_str = json.dumps(file_list, indent=4)
# print(json_formatted_str)
update_macros_md(file_list)
update_macros_yml(file_list)
def parse_macros(file_path):
"""
指定されたSQLファイルからマクロ定義を解析し、マクロ情報をリストとして返す。
Args:
file_path (str): 解析するSQLファイルのパス
Returns:
list: マクロ情報を含む辞書のリスト
"""
# ファイル名を取得(拡張子なし)
file_name = os.path.splitext(os.path.basename(file_path))[0]
# マクロ情報を格納するリスト
macros = []
# ファイルを開いて内容を読み取る
with open(file_path, "r", encoding="utf-8") as f:
content = f.read()
# マクロ定義を正規表現で抽出
macro_pattern = r"{%-?\s*macro\s+(\w+)\((.*?)\)\s*-?%}"
matches = re.findall(macro_pattern, content)
for macro_name, args in matches:
# 引数を分割してリスト化
arguments = [{"name": convert_assignment_to_default(arg.strip())} for arg in args.split(",") if arg.strip()]
# 各マクロ情報を辞書形式で追加
macros.append({"name": macro_name, "description": f'{{{{ doc("{file_name}") }}}}', "arguments": arguments})
return macros
def convert_assignment_to_default(text):
"""
引数が `key = value` の形式の場合、`(key default: value)` の形式に変換する。
Args:
text (str): 変換する文字列
Returns:
str: 変換後の文字列
"""
match = re.match(r"(\w+)\s*=\s*(.+)", text)
if match:
key, value = match.groups()
return f"{key} (default: {value})"
return text # `=` がなければそのまま返す
def update_yaml_file(file_path, new_data):
"""
YAMLファイルを更新または作成する。
Args:
file_path (str): 更新または作成するYAMLファイルのパス
new_data (dict): 新しいデータ
"""
# 既存データの読み込み
if os.path.exists(file_path):
with open(file_path, "r", encoding="utf-8") as f:
existing_data = yaml.safe_load(f) or {} # `None` の場合は `{}` を設定
else:
existing_data = {}
# `macros` が存在しない場合、空リストを設定
existing_macros = existing_data.get("macros", [])
# 既存のマクロ名を取得(重複チェック用)
existing_macro_names = {macro["name"] for macro in existing_macros}
# 新しいデータをマージ(重複を避ける)
for new_macro in new_data["macros"]:
if new_macro["name"] not in existing_macro_names:
existing_macros.append(new_macro)
# 更新したデータを保存
updated_data = {"version": 2, "macros": existing_macros}
# YAMLをフォーマットして書き込み
yaml_text = yaml.dump(updated_data, default_flow_style=False, sort_keys=False, allow_unicode=True)
# 各要素の間に改行を入れる
yaml_text = yaml_text.replace("\nmacros:", "\n\nmacros:") # `macros` の前に改行を入れる
# ファイルに書き込む場合
with open(file_path, "w", encoding="utf-8") as f:
f.write(yaml_text)
def generate_yaml_from_sql(sql_files):
"""
指定されたSQLファイルリストからYAML構造を生成する。
Args:
sql_files (list): 解析するSQLファイルのパスのリスト
Returns:
dict: 生成されたYAML構造
"""
all_macros = []
for sql_file in sql_files:
macros = parse_macros(sql_file)
all_macros.extend(macros) # 解析結果を統合
yaml_structure = {"version": 2, "macros": all_macros}
return yaml_structure
def get_files(directory):
"""
指定されたディレクトリ内のSQLファイルを収集する。
Args:
directory (str): 検索するディレクトリのパス
Returns:
dict: ディレクトリパスをキー、SQLファイル名のリストを値とする辞書
"""
entries = {}
for root, _, files in os.walk(directory):
sql_files = []
for file in files:
if file.endswith(".sql"):
sql_files.append(file)
entries[root] = sql_files
return entries
def update_macros_md(data):
"""
指定されたデータに基づいて macros.md ファイルを更新または作成する。
Args:
data (dict): ディレクトリパスをキー、ファイル名のリストを値とする辞書
"""
for directory, files in data.items():
md_entries = {}
for file in files:
file_name = os.path.splitext(file)[0]
md_entries[file_name] = f"{{% docs {file_name} %}}\n\n{{% enddocs %}}\n"
# macros.md ファイルのパス
md_file = os.path.join(directory, "macros.md")
# macros.md が存在するか確認
if not os.path.exists(md_file):
# 存在しない場合は新規作成して書き込む
with open(md_file, "w") as f:
f.write("\n".join(md_entries.values())) # リストを改行で結合して書き込み
else:
# macros.md が存在する場合、file_name がすでに含まれているか確認
with open(md_file, "r") as f:
existing_content = f.read()
# file_name が含まれていない場合のみ書き込む
new_entries = [
entry for file_name, entry in md_entries.items() if f"{{% docs {file_name} %}}" not in existing_content
]
if new_entries: # 新しいエントリがある場合のみ追記
with open(md_file, "a") as f:
f.write("\n".join(new_entries))
def update_macros_yml(data):
"""
指定されたデータに基づいて macros.yml ファイルを更新または作成する。
Args:
data (dict): ディレクトリパスをキー、ファイル名のリストを値とする辞書
"""
for directory, files in data.items():
yml_file = os.path.join(directory, "macros.yml")
sql_files = [os.path.join(directory, file) for file in files]
if not os.path.exists(yml_file):
result = generate_yaml_from_sql(sql_files)
update_yaml_file(yml_file, result)
if __name__ == "__main__":
main()
{% docs ~ %}
{% enddocs %}
version: 2
macros:
- name: ~
description: '{{ doc("~") }}'
arguments:
- name: ~
各フォルダにmdおくとdocs-paths: ["docs"]
の設定を衝突する
docs-paths: ["docs"]
を指定していないとCustom project-level overviewsをoverviews.md以外のmdで保存して認識しなくなるのでmacros.mdの生成を断念

ymlにコメントとしてmacroを書く時、{% raw %}{% endraw %}で挟まないとCompilation Errorになって困った

社内システムとか、リリース前データを除外する処理をマクロで共通化する例
{% macro systemname_release_filter(date_column) %}
date({{ date_column }}) between "2025-03-04" and current_date("Asia/Tokyo") - 1
{% endmacro %}
{#
where {{ systemname_release_filter("created_at_jst") }} -- リリース後に絞る
#}

array
{% macro array_functions__array_except(a1, a2) %}
(
select array_agg(distinct i order by i)
from unnest(coalesce({{ a1 }}, [])) as i
left join unnest(coalesce({{ a2 }}, [])) as j on i = j
where j is null
)
{% endmacro %}
{#
# サンプル
select {{ array_functions__array_except("[1,2,3,4,5]", "[3,4,5,6,7]") }} as result
--> [1, 2]
#}