关于 dbt-sf-to-bq-translator
用于将 Snowflake dbt SQL 模型迁移为 BigQuery SQL 的技能,面向负责 Google Cloud 数据迁移的团队。它通过脚本协调翻译和后处理,同时保留 dbt Jinja 结构并应用标准化规则。
主要功能
- 01批量调用 BigQuery Translation Service
- 02保留并恢复 dbt Jinja 结构
- 03转换 JSON、ARRAY 和 STRUCT 表达式
- 04处理 config 块、宏和 SQL 边界情况
- 05写入版权头并复制 YAML 配置
适用场景
- 01将 Snowflake dbt 项目迁移到 BigQuery
- 02利用表和列元数据辅助 SQL 翻译
- 03标准化 BigQuery 模型中的 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。
- 标准化与合规:执行 Google 专属标准,包括在每个文件最顶部添加版权声明、显式类型转换、标准化 JSON 提取,以及通过
QUALIFY和_extracted_at进行去重。 - 工作流集成:保留 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)。 - (可选)包含源数据库元数据的目录或
.zip文件的本地路径(例如columns.csv或tables.csv)。 收到信息后,在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 存储桶名称。 --locationus用于执行 BigQuery Translation Service 的 Google Cloud 区域(例如, us、eu)。--metadataNone可选路径,指向包含源 tables.csv和columns.csv的目录或.zip存档。--array-cast-modearray_string用于 BQMS ARRAY<JSON>转换的目标类型。默认使用array_string(ARRAY<STRING>);如需适配源表架构,可使用string(STRING)、json_extract_string_array或preserve作为备用选项。--struct-columnsNone可选的逗号分隔列表,列出要从 JSON_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 提取清理、未实现操作码清理、宏审计、大小写与 JOIN 规范化,以及版权标头强制检查。
-
详细翻译生命周期(由脚本执行):
- 使用占位符编译为标准 SQL(翻译前):
- 读取原始源 dbt
.sql文件。提取并移除每个文件顶部的{{ config(...) }}头部块。 - 将 dbt 宏调用替换为符合标准 SQL 的占位符标识符,以避免 BigQuery Translation Service 抛出语法错误:
- 将
{{ 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/ - 创建
migration_config.yaml,指定snowflakeDialect为源方言,bigqueryDialect为目标方言(如果提供了metadata.zip,则将schemaPath指向它)。 - 触发翻译工作流:
gcloud bq migration-workflows create --location=<region> --config-file=migration_config.yaml --no-async - 从 Cloud Storage 下载已翻译的 GoogleSQL 文件:
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 列上的json_value转换为col[SAFE_OFFSET(idx)].field,并清理error_unimplemented操作码。 - 版权声明头部位置:在配置块上方,将必需的 Google 版权声明头部添加到文件最顶部。
- 基于 AST 的配置转换:解析原始
- 对已翻译的 BigQuery SQL 文件进行高级后处理:
- 写入新的 BigQuery dbt 文件并复制 YAML 配置:
- 保存新组装且兼容 BigQuery 的文件,同时保留目录结构。此外,将输入目录中的所有
.yml/.yaml文件复制到输出目录。
- 保存新组装且兼容 BigQuery 的文件,同时保留目录结构。此外,将输入目录中的所有
- 使用占位符编译为标准 SQL(翻译前):
-
在任务文件末尾添加摘要。
-
交接前,征求用户对结果的批准。根据用户反馈进行必要的修改。
-
在任务文件中标记任务已完成。
Mandates & Behavioral Rules
1. Mandatory File Header
每个翻译文件必须在最顶部以以下精确标头开头:
# 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. Standardized JSON & Array Extraction
在 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(...)展开。默认将 BQMS 中从 Snowflake::array转换而来的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,并将所有残留的 BQMSerror_unimplemented或 16MB 的substr(string(...), 1, 16777216)包装器替换为col.field(对于STRUCT列)或JSON_VALUE(col, '$.field')(对于JSON列;参见 dbt_migration_patterns.md 的第 7–8 节)。
3. Explicit Type Safety
- 比较:如果类型不完全相同,连接或筛选条件两侧都必须使用
SAFE_CAST。必要时使用AS STRING,确保通用比较安全。 - ID 字段:所有系统 ID(例如
ticket_id、user_id)优先使用INT64。 - 空值处理:
- 对于占位列,始终使用显式类型转换:
SAFE_CAST(NULL AS TYPE)。
- 对于占位列,始终使用显式类型转换:
4. Prescriptive String & Date Functions
- 截断:使用
LEFT(col, length)或SUBSTR(col, 1, length)。 - 搜索:使用
LOWER(col) LIKE '%pattern%',而不是REGEXP_CONTAINS。 - 日期加法:使用
DATE_ADD(SAFE_CAST(col AS DATETIME), INTERVAL num HOUR)。
5. Mandatory Deduplication & Joins
- 去重:如果源模型要求按主键去重:
- 规则:在基础 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() %} 代码块保持可用。除非源文件中已有历史数据联合查询或其他自定义宏/表,否则不要添加。
输出
你将输出与 BigQuery 兼容的 dbt SQL 模型,存放在 migration_plan/[mig_prefix]/translated_models/ 下。翻译后的文件必须严格遵循结构模式和方言格式。
翻译示例请参阅:dbt_migration_patterns.md
约束
在移交给 root agent 之前,先征得用户对翻译后模型的认可。根据用户的要求进行修改后,再移交给 root agent。