dbt-sf-to-bq-translator について
Snowflake の dbt SQL モデルを Google Cloud 上の BigQuery に移行するチーム向けのスキルです。付属スクリプトで翻訳と後処理を行い、dbt の Jinja 構文を保ちながら標準化します。
主な機能
- 01BigQuery Translation Service による一括実行
- 02dbt Jinja 構文の保持と復元
- 03JSON、ARRAY、STRUCT 式の変換
- 04config ブロック、マクロ、SQL の例外処理
- 05著作権ヘッダーの追加と YAML 設定のコピー
活用シーン
- 01Snowflake の dbt プロジェクトを BigQuery に移行
- 02テーブルと列のメタデータを翻訳に利用
- 03BigQuery モデルの JSON と配列アクセスを標準化
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ google/ skills --skill dbt-sf-to-bq-translatorインストール後、Claude Code で/dbt-sf-to-bq-translatorを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/attribution.mddbt_migration_patterns.mdscripts/bulk_translate_via_gcloud.pydbt_translator.py詳細
dbt Snowflake から BigQuery へのトランスレーター
担当する内容:
- 方言の変換:Snowflake の dbt SQL モデルを、Google Cloud 上の標準化された BigQuery SQL に変換します。
- 標準化とコンプライアンス:各ファイルの先頭に著作権ヘッダーを付けること、明示的な型キャスト、標準化された JSON 抽出、
_extracted_atを使用したQUALIFYによる重複排除など、Google 固有の標準を適用します。 - ワークフローへの統合:dbt の Jinja 構文を保持し、最終的な BigQuery 互換モデルを保存します。
migration_plan/[mig_prefix]/tasks.md に記載された指示に従ってください。作業中に進捗を追記し、最後にタスクファイルへ概要を記載して、人間の監督者が進捗を確認できるようにします。エラーから復旧する際は、タスクファイルを確認してください。
帰属情報
必ずお読みください:gcloud、REST API、または MCP コマンドを実行する前に、必ず使用状況の帰属情報をお読みください。ターミナルで直接実行するコマンド(生成スクリプト、Terraform、ユーザーコード内では実行しない場合)では、gcloud の前に
CLOUDSDK_METRICS_REQUEST_ATTRIBUTION="agent-skills/1.0 (skill:dbt-sf-to-bq-translator)"
を付け、User-Agent ヘッダーに
agent-skills/1.0 (skill:dbt-sf-to-bq-translator) を追加してください。
前提条件と環境のセットアップ
変換を開始する前に、Google Cloud 環境が適切に設定されていることを確認してください。
- Google Cloud SDK:まだインストールされていない場合は、Google Cloud SDK をインストールします。
- 認証:CLI セッションを認証します。
gcloud auth login gcloud auth application-default login - プロジェクトの設定:有効な Google Cloud プロジェクトを設定します。
gcloud config set project {project_id} - 請求先アカウント:対象プロジェクトに有効な Google Cloud 請求先アカウントが関連付けられていることを確認します。
- 必要な API の有効化:BigQuery、Migration、Storage の各サービスが有効になっていることを確認します。
gcloud services enable bigquerymigration.googleapis.com storage.googleapis.com bigquery.googleapis.com - リージョンの選択:希望するコンピューティング/BigQuery リージョンを設定します(推奨されるデフォルト値:
us-central1またはus)。Google Cloud のロケーションを参照してください。gcloud config set compute/region us-central1
手順
-
初期化とセットアップ:
mig_prefixが定義されていない場合、またはユーザーが新しい変換プロジェクトの開始を希望する場合は、まずユーザーに以下を必ず確認してください。- 移行プロジェクト名(例:
my_migration_project)。 - Snowflake SQL ファイルを含む入力ディレクトリ。
- BigQuery SQL ファイルの保存先となる出力ディレクトリ。
- 変換アセットのステージングに使用する Cloud Storage バケット名。
- Google Cloud リージョン(例:
usまたはeu)。 - (任意)ソースデータベースのメタデータ(
columns.csvやtables.csvなど)を含むディレクトリまたは.zipファイルのローカルパス。 情報を受け取ったら、移行手順を表す未完了タスクを含むチェックリストファイルをmigration_plan/[mig_prefix]/tasks.mdに作成します。
- 移行プロジェクト名(例:
-
同梱スクリプトによる自動実行: 同梱の変換スクリプト
scripts/bulk_translate_via_gcloud.pyを使用して、決定的なエンドツーエンドの移行を実行します。python3 scripts/bulk_translate_via_gcloud.py \ --input {input_dir} \ --output {output_dir} \ --bucket {gcs_bucket} \ --location {region} \ [--metadata {metadata_path}] \ [--array-cast-mode {array_cast_mode}] \ [--struct-columns {struct_columns}]フラグ デフォルト 説明 --input(必須) ソースの Snowflake dbt .sqlおよび.ymlファイルを含むローカルディレクトリです。--output(必須) 変換後の BigQuery dbt .sqlおよび.ymlファイルの出力先ディレクトリです。--bucket(必須) 変換の入力と出力を一時保存するために使用する Cloud Storage バケット名です。 --locationusBigQuery Translation Service の実行に使用する Google Cloud リージョンです(例: us、eu)。--metadataNoneソースの tables.csvおよびcolumns.csvを含むディレクトリまたは.zipアーカイブの省略可能なパスです。--array-cast-modearray_stringBQMS の ARRAY<JSON>キャストに対する変換先です。デフォルトではarray_string(ARRAY<STRING>)を使用します。ソーステーブルのスキーマに応じて、代替手段としてstring(STRING)、json_extract_string_array、またはpreserveを使用できます。--struct-columnsNoneJSON_VALUEからネイティブのドット記法(col.field)に変換する、追加の BigQuerySTRUCT列名をカンマ区切りで指定する省略可能なリストです(例:struct_col,record_col)。scripts/に含まれる移行ツールは、以下の一連の処理を実行します。scripts/bulk_translate_via_gcloud.py:前処理、Cloud Storage へのアップロード、BigQuery Translation Service の呼び出し、ダウンロード、後処理、YAML 設定のコピーを自動化し、エンドツーエンドの一括移行を統括します。scripts/dbt_translator.py:config(...)用の決定論的な AST パーサー、Jinja プレースホルダーのマスキングと復元、JSON/STRUCT/ARRAY 抽出のサニタイズ、未実装オペコードのクリーンアップ、マクロの監査、大文字・小文字と結合の標準化、著作権ヘッダーの適用を含む、コア変換ライブラリです。
-
詳細な翻訳ライフサイクル(スクリプトによって実行):
- プレースホルダーを使用して標準 SQL にコンパイル(翻訳前):
- 元の dbt
.sqlファイルを読み込みます。各ファイルの先頭にある{{ config(...) }}ヘッダーブロックを抽出して削除します。 - BigQuery Translation Service で構文エラーが発生しないよう、dbt マクロ呼び出しを標準 SQL に準拠したプレースホルダー識別子に置き換えます。
{{ source('src_name', 'table_name') }}を_DBT_SOURCE_src_name_DBTSEP_table_name_に置き換えます。{{ ref('model_name') }}を_DBT_REF_model_name_に置き換えます。
- Snowflake の角括弧記法(
col['prop']->col:prop)、::variant:field、Jinja の{% else %}/ループ内のUNION ALL分岐を正規化し、翻訳前に Jinja の波括弧({{ ... }})を取り除くことで、トランスパイラーが 100%有効な Snowflake 方言の SQL を処理できるようにします。
- 元の dbt
- SQL ファイルを分離する:
- 前処理済みの Jinja を含まないファイルを、Cloud Storage へのアップロードに備えてステージング入力ディレクトリに保存します。
- メタデータを前処理し、BigQuery Translation Service で SQL を翻訳する:
- メタデータのパスが指定されている場合、検出された dbt モデル/ソースに一致するテーブルエントリを
columns.csvとtables.csv内のプレースホルダー名に対応付け、名前空間の解決エラーを防ぐためカタログ名をクリアし、metadata.zipにまとめて Cloud Storage にアップロードします。 - ステージング SQL ファイルを Cloud Storage にアップロードします。
gcloud storage cp <staging_input_dir>/*.sql gs://[YOUR_BUCKET]/migration_input/ - ソースを
snowflakeDialect、ターゲットをbigqueryDialectとして指定するmigration_config.yamlを作成します(メタデータが指定されている場合は、schemaPathがmetadata.zipを指すようにします)。 - 翻訳ワークフローを開始します。
gcloud bq migration-workflows create --location=<region> --config-file=migration_config.yaml --no-async - 翻訳済みの GoogleSQL ファイルを Cloud Storage からダウンロードします。
gcloud storage cp gs://[YOUR_BUCKET]/migration_output/*.sql <translated_output_dir>/
- メタデータのパスが指定されている場合、検出された dbt モデル/ソースに一致するテーブルエントリを
- プレースホルダーを復元し、dbt のロジックを再度組み込む:
- 翻訳済みの BigQuery SQL ファイルに対して、高度な後処理を行います。
- AST ベースの設定変換:元の
{{ config(...) }}ブロックを解析し、copy_grants、transient、secureなどの Snowflake 固有のパラメーターを削除します。pre_hookとpost_hookをサニタイズし、ALTER ICEBERG TABLE ... REFRESH、UNSET SECURE、COPY INTOなどの無効な Snowflake コマンドを取り除き、有効なものは保持します。 - 参照リゾルバー(名前空間の解決):SQL 内の FROM 句と JOIN 句にあるハードコードされた Snowflake データベース/スキーマのテーブルパスをスキャンし、検出されたプロジェクトのモデルおよびソースを照合して、ネイティブの dbt
{{ ref(...) }}または{{ source(...) }}マクロに戻します。 - マクロと構文の監査:すべての
{{ ... }}Jinja 式をスキャンし、カスタムまたは許可リストにないデータベース固有のマクロについて警告を記録します。また、これらのブロックを Snowflake 固有の構文(例:::date、dateadd、to_date)について監査し、見落とされたりマスクされたりした可能性のあるものを示す警告コメントをファイル内に直接記載します。 - 括弧の対応を考慮した SQL のエッジケースのサニタイズ:括弧の対応を解析して、日付キャストのサフィックス(
::date->CAST(... AS DATE))、日時キャストのサフィックス(::timestamp->CAST(... AS TIMESTAMP))、浮動小数点数キャストのサフィックス(::float->CAST(... AS FLOAT64))、ネストされたdateadd(...)呼び出し、および制御ブロックの内外にある区間(- interval '5 month')を変換します。CTE の周囲にずれた Jinja タグを修正し、16MB のsubstr(string(...), 1, 16777216)ラッパーを削除します。 - 配列型と STRUCT アクセスの正規化:型指定のない BQMS の
ARRAY<JSON>キャストをARRAY<STRING>(または設定済みの--array-cast-modeのターゲット)に変換し、STRUCT列に対するjson_valueを直接のドット記法(col.field)に、ARRAY<STRUCT>列に対するものをcol[SAFE_OFFSET(idx)].fieldに変換し、error_unimplementedオペコードをクリーンアップします。 - 著作権ヘッダーの配置:必須の Google 著作権ヘッダーを、設定ブロックより前のファイルの最上部に追加します。
- AST ベースの設定変換:元の
- 翻訳済みの BigQuery SQL ファイルに対して、高度な後処理を行います。
- 新しい BigQuery dbt ファイルに書き込み、YAML 設定をコピーする:
- ディレクトリ構造を維持しながら、新しく組み立てた BigQuery 互換ファイルを保存します。また、入力ディレクトリ内のすべての
.yml/.yamlファイルを出力ディレクトリにコピーします。
- ディレクトリ構造を維持しながら、新しく組み立てた BigQuery 互換ファイルを保存します。また、入力ディレクトリ内のすべての
- プレースホルダーを使用して標準 SQL にコンパイル(翻訳前):
-
最後に、タスクファイルに概要を記載します。
-
引き渡す前に、成果物についてユーザーの承認を求めます。ユーザーから必要な変更を受けたら、適用します。
-
タスクファイルでタスクを完了済みにします。
必須事項と行動規則
1. 必須のファイルヘッダー
翻訳するファイルはすべて、先頭に以下の正確なヘッダーを必ず記載してください。
# Copyright 2026 Google. This software is provided as-is, without warranty or
# representation for any use or purpose. Your use of it is subject to your
# agreement with Google.
2. 標準化された JSON と配列の抽出
BigQuery SQL では Snowflake のコロン記法(:)を使用しないでください。BigQuery Standard SQL は半構造化フィールドへのアクセスにコロン構文を認識しないため、構文エラーが発生します。半構造化データの検索には BigQuery の標準関数を使用し、詳しいパターンとエラーへの対処方法については dbt_migration_patterns.md を参照してください。
- スカラー値と文字列化された JSON の抽出:ネイティブの
JSON列にはJSON_VALUE(json_col, '$.path')を使用します(配列インデックスの前にドットを入れず、例:$.items[1])。文字列化された JSON 列にはCAST(JSON_EXTRACT_SCALAR(SAFE.PARSE_JSON(CAST(source.col AS STRING)), '$.path') AS TYPE)を使用します。厳密な型指定が必要な場合は、型指定した抽出をSAFE_CAST(... AS INT64|BOOL|TIMESTAMP|NUMERIC)で囲みます。 - 配列の抽出、展開、型指定なし配列(
::array):JSON_EXTRACT_STRING_ARRAYまたはJSON_EXTRACT_ARRAYを使用し、CROSS JOIN UNNEST(...)で展開します。Snowflake の::arrayから BQMS のARRAY<JSON>キャストへの変換では、デフォルトでARRAY<STRING>にマッピングします(ソーススキーマに応じてSTRINGまたはJSON_EXTRACT_STRING_ARRAYを使用してください。詳しくは dbt_migration_patterns.md のセクション 3 を参照)。 - JSON タイムスタンプ(
'Z'で終わる ISO-8601 形式):JSON のタイムスタンプ抽出にはTIMESTAMPをキャストします(CAST(JSON_EXTRACT_SCALAR(json_col, '$.updated_at') AS TIMESTAMP))。'Z'で終わる ISO-8601 形式の文字列はCAST(... AS DATETIME)で実行時エラーになるためです。下流でDATETIMEが必要な場合は、いったんTIMESTAMPにキャストしてから変換します(詳しくは dbt_migration_patterns.md のセクション 5 を参照)。 - BigQuery の
STRUCT列とARRAY<STRUCT>列:ネイティブのSTRUCT列には直接ドット記法(col.field、例:source.struct_col.count)を使用します。ARRAY<STRUCT>列にはsource.col[SAFE_OFFSET(0)].field(またはJSON_VALUE(TO_JSON(source.col), '$[0].field'))を使用します。JSON_VALUE()とCAST(... AS JSON)はSTRUCT型を受け付けないためです(詳しくは dbt_migration_patterns.md のセクション 6 を参照)。 - Snowflake のブラケット記法(
col['field'])とerror_unimplemented:前処理中にcol['field']をcol:fieldに正規化し、残っている BQMS のerror_unimplementedまたは 16MB のsubstr(string(...), 1, 16777216)ラッパーは、STRUCT列の場合はcol.fieldに、JSON列の場合はJSON_VALUE(col, '$.field')に置き換えます(詳しくは dbt_migration_patterns.md のセクション 7〜8 を参照)。
3. 明示的な型安全性
- 比較:結合またはフィルターの両辺の型が一致しない場合は、必ず両辺で
SAFE_CASTを使用します。必要に応じて、比較の安全性を広く確保できるAS STRINGを使用します。 - ID フィールド:すべてのシステム ID(例:
ticket_id、user_id)にはINT64を優先して使用します。 - NULL の処理:
- プレースホルダー列には、必ず明示的な型キャストを使用します:
SAFE_CAST(NULL AS TYPE)。
- プレースホルダー列には、必ず明示的な型キャストを使用します:
4. 文字列関数と日付関数の指定
- 切り詰め:
LEFT(col, length)またはSUBSTR(col, 1, length)を使用します。 - 検索:
REGEXP_CONTAINSの代わりにLOWER(col) LIKE '%pattern%'を使用します。 - 日付の加算:
DATE_ADD(SAFE_CAST(col AS DATETIME), INTERVAL num HOUR)を使用します。
5. 重複排除と結合の必須ルール
- 重複排除:ソースモデルで主キーによる重複排除が必要な場合:
- ルール:ベース CTE で
row_number() over (partition by [PRIMARY_KEY] order by [TIMESTAMP] desc) as rn列を使用し、その CTE に直接qualify rn = 1を適用します。
- ルール:ベース CTE で
- 結合:カスタムフィールドまたは属性テーブル(例:
exploded_arrayパターン)との結合にはLEFT JOINを使用し、レコードが失われないようにします。
6. Jinja 構文を保持する
{{ config(...) }}、{{ ref(...) }}、{{ source(...) }} は変更しないでください。{% if is_incremental() %} ブロックが機能する状態を保ってください。ソースファイルに記載されていない限り、過去のデータを結合する処理や、その他のカスタムマクロ/テーブルを追加しないでください。
出力
migration_plan/[mig_prefix]/translated_models/ に、BigQuery 互換の dbt SQL モデルを出力してください。翻訳したファイルは、構造パターンと方言の書式に厳密に従う必要があります。
翻訳例については、dbt_migration_patterns.md を参照してください。
制約
ルートエージェントに引き渡す前に、まず翻訳したモデルについてユーザーの承認を得てください。ユーザーの要望を反映した後、ルートエージェントに引き渡してください。