数据库
将 Snowflake dbt 模型迁移到…
当你需要把 Snowflake 上的 dbt SQL 模型批量迁移为符合 Google 规范的 BigQuery dbt 模型时使用。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 38/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 55/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 60/100 ×0.15
适用场景
将 Snowflake dbt 模型迁移特定
解决思路
- 初始化项目:向用户询问迁移项目名(mig_prefix)、Snowflake SQL 输入目录、BigQuery SQL 输出目录、用于暂存翻译资产的 GCS Bucket 名、GCP 区域、以及可选的源数据库元数据路径(columns.csv/tables.csv 所在目录或 .zip),然后在 migration_plan/[mig_prefix]/tasks.md 创建未勾选的任务清单。
- 配置 GCP 环境:安装 Google Cloud SDK,执行 gcloud auth login 和 gcloud auth application-default login,用 gcloud config set project {project_id} 设置项目,确认项目已绑定有效计费账号,执行 gcloud services enable bigquerymigration.googleapis.com storage.googleapis.com bigquery.googleapis.com 启用所需 API,并用 gcloud config set compute/region us-central1 设置区域。
- 运行捆绑脚本执行端到端迁移:python3 scripts/bulk_translate_via_gcloud.py --input <input_dir> --output <output_dir> --bucket <gcs_bucket> --location <region> [--metadata <metadata_path>],该脚本负责预处理、GCS 上传、调用 BigQuery Translation Service、下载、后处理和 YAML 配置复制;核心翻译逻辑在 scripts/dbt_translator.py 中。
- 预翻译编译:读取原始 dbt .sql 文件,从文件顶部提取并剥离 {{ config(...) }} 头块;把 dbt 宏调用替换为标准 SQL 兼容的占位符,将 {{ source('src_name', 'table_name') }} 替换为 _DBT_SOURCE_src_name_DBTSEP_table_name_,将 {{ ref('model_name') }} 替换为 _DBT_REF_model_name_,从而在翻译前消除所有 Jinja 花括号,让转译器只处理 100% 合法的 Snowflake 方言 SQL。
- 隔离 SQL 文件:把预处理后不含 Jinja 的文件保存到暂存输入目录,准备上传到 GCS。
- 预处理元数据并调用 BigQuery Translation Service:若提供元数据路径,把匹配到的 dbt 模型/源映射到 columns.csv 和 tables.csv 中的占位符名,清空 catalog 名以避免命名空间解析错误,打包为 metadata.zip 并上传到 GCS;用 gcloud storage cp <staging_input_dir>/*.sql gs://[YOUR_BUCKET]/migration_input/ 上传暂存 SQL;创建 migration_config.yaml,指定 snowflakeDialect 为源、bigqueryDialect 为目标(如有 metadata.zip 则设置 schemaPath);用 gcloud bq migration-workflows create --location=<region> --config-file=migration_config.yaml --no-async 触发翻译;用 gcloud storage cp gs://[YOUR_BUCKET]/migration_output/*.sql <translated_output_dir>/ 下载翻译后的 GoogleSQL 文件。
- 恢复占位符并重新嵌入 dbt 逻辑:对翻译后的 BigQuery SQL 做高级后处理,包括:AST 解析原始 {{ config(...) }} 块,剥离 copy_grants、transient、secure 等 Snowflake 专有参数,清理 pre_hook/post_hook 中如 ALTER ICEBERG TABLE ... REFRESH、UNSET SECURE 等非法 Snowflake 命令但保留合法命令;扫描 FROM 和 JOIN 中硬编码的 Snowflake 库/模式表路径,对照已发现的模型和源映射回原生 dbt {{ ref(...) }} 或 {{ source(...) }} 宏;审计所有 {{ ... }} Jinja 表达式,对自定义/非白名单的数据库专有宏记录警告,并检查其中可能被跳过或掩码的 Snowflake 专有语法(如 ::date、dateadd、to_date),在文件中直接写入警告注释;用平衡括号解析器转换控制块内外的日期转换后缀(::date -> CAST(... AS DATE))、时间戳转换后缀(::timestamp -> CAST(... AS TIMESTAMP))、嵌套 dateadd(...) 调用和 interval(- interval '5 month');在文件最顶部、config 块之上前置强制性的 Google 版权头。
- 写入新的 BigQuery dbt 文件并复制 YAML 配置:按原目录结构保存组装好的 BigQuery 兼容文件,同时把输入目录中所有 .yml/.yaml 文件复制到输出目录。
- 在 tasks 文件中写入总结,交付前向用户请求对结果的批准,按用户要求做必要修改,最后在 tasks 文件中标记任务完成。
- 强制文件头:每个翻译后的文件必须以 # 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. 开头。
- 标准化 JSON 提取:禁止使用 Snowflake 冒号语法或 BigQuery JSON_VALUE,统一使用 CAST(JSON_EXTRACT_SCALAR(json_column, '$.path') AS TYPE);ID(主/外键)用 AS INT64(不要用 NUMERIC),布尔标志用 AS BOOL,字符串用 AS STRING(除非源中需显式处理空/ null 字符串否则不要包 NULLIF),时间戳用 AS TIMESTAMP。
- 显式类型安全:join 或 filter 两侧类型不一致时始终对两侧使用 CAST,必要时用 AS STRING 保证通用比较安全;所有系统 ID(如 ticket_id、user_id)优先用 INT64;占位列始终用显式类型转换 CAST(NULL AS TYPE)。
- 规定的字符串与日期函数:截断用 LEFT(col, length) 或 SUBSTR(col, 1, length);搜索用 LOWER(col) LIKE '%pattern%' 而非 REGEXP_CONTAINS;日期加法用 DATE_ADD(CAST(col AS DATETIME), INTERVAL num HOUR)。
- 强制去重与 join:若源模型需按主键去重,在基础 CTE 中用 row_number() over (partition by [PRIMARY_KEY] order by [TIMESTAMP] desc) as rn 列,并在该 CTE 上直接应用 qualify rn = 1;join 自定义字段或属性表(如 exploded_array 模式)时用 LEFT JOIN 以防丢记录。
- 保留 Jinja 结构:不要改动 {{ config(...) }}、{{ ref(...) }}、{{ source(...) }},保持 {% if is_incremental() %} 块可用,除非源文件中已存在,否则不要注入历史数据 union 或其他自定义宏/表。
- 输出位置:在 migration_plan/[mig_prefix]/translated_models/ 下输出 BigQuery 兼容的 dbt SQL 模型,翻译文件必须严格遵循结构模式和方言格式,可参考 references/dbt_migration_patterns.md 中的示例。
适用边界 · 注意事项
- 不要用于通用 BigQuery 查询编写。
- 不要用于非 Snowflake 的 SQL 迁移。