dbt快照生成重复dbt_scd_id,但源查询无重复的排查求助
问题分析与解决思路
针对你遇到的dbt快照生成重复dbt_scd_id、唯一键测试失败的问题,以下是核心原因排查和解决方法:
1. 自定义宏snapshot_latest_record逻辑存在漏洞
这是最常见的根源,即使你验证了最新导出数据无重复,也可能是宏的逻辑没有正确过滤出单次完整导出的数据集:
- 先直接执行宏生成的SQL,验证输出是否真的无重复ID:
SELECT id, COUNT(*) AS row_count FROM ({{ snapshot_latest_record('your_source_table') }}) AS latest_data GROUP BY id HAVING row_count > 1 - 如果查询返回结果,说明宏的逻辑错误:
- 错误示例:如果宏是按ID分组取
MAX(inserted_at_utc),可能会意外包含跨批次的重复ID(比如某个ID在旧批次未被新批次覆盖的极端情况); - 正确逻辑:应该直接取最新一次完整导出的所有行,即过滤
inserted_at_utc等于最大导出时间的数据集:{% macro snapshot_latest_record(source_table) %} SELECT * FROM {{ source_table }} WHERE inserted_at_utc = (SELECT MAX(inserted_at_utc) FROM {{ source_table }}) {% endmacro %}
- 错误示例:如果宏是按ID分组取
2. dbt快照配置与宏的兼容性问题
即使宏输出正确,快照的check策略配置也可能引发重复:
- 确认
unique_key严格设置为id,且check_cols和hard_delete配置逻辑自洽:{% snapshot snap_boond__agencies %} {{ config( target_schema='snapshots', strategy='check', unique_key='id', check_cols='all', hard_delete=true, ) }} {{ snapshot_latest_record('source.boond.agencies') }} {% endsnapshot %} - 注意:如果
check_cols设为all,dbt会对比包括inserted_at_utc在内的所有列,若每次导出的inserted_at_utc不同,即使其他列无变化,dbt也会生成新快照版本,但不会导致dbt_scd_id重复——除非同一次快照运行中同一个ID被多次处理。
3. dbt_scd_id生成逻辑的冲突
dbt_scd_id是dbt自动生成的主键,公式大致为:
MD5(CONCAT(COALESCE(CAST(id AS VARCHAR), 'null'), '|', COALESCE(CAST(dbt_valid_from AS VARCHAR), 'null')))
如果同一次快照运行中,同一个ID被宏返回多次,dbt_valid_from(快照运行时间)相同,就会生成完全一致的dbt_scd_id,触发主键重复错误。这直接说明宏的输出存在重复ID,必须优先修复宏逻辑。
4. 源表导出的隐性重复
即使你认为最新导出数据无重复,也可能存在隐性问题:
- 直接查询源表最新批次数据,验证是否真的无重复:
SELECT id, COUNT(*) AS row_count FROM your_source_table WHERE inserted_at_utc = (SELECT MAX(inserted_at_utc) FROM your_source_table) GROUP BY id HAVING row_count > 1 - 如果有结果,说明导出工具存在重复插入的问题,需要修复源数据的导出逻辑。
5. 快照运行的并发冲突
如果快照运行时,源表的全量导出事务尚未提交,宏可能查询到不完整的中间数据,导致临时重复ID。解决方法:
- 调整快照运行时机,确保在源表导出完成后再执行快照;
- 在Snowflake中通过查询
QUERY_HISTORY确认导出事务的提交状态。
内容的提问来源于stack exchange,提问作者user15915737




