无唯一键时如何在DBT中实现基于日期范围的增量DELETE+INSERT操作?
无唯一键时如何在DBT中实现基于日期范围的增量DELETE+INSERT操作?
嘿,刚巧我之前帮同事解决过几乎一模一样的问题!没有唯一键的情况下,DBT默认的merge增量策略确实用不了,但咱们完全可以靠自定义增量逻辑来复刻你原来的DELETE+INSERT流程,完美贴合你要的日期范围更新需求。
下面直接上可落地的方案,一步步来:
1. 先把模型配置为增量模型
首先在你的模型SQL文件开头,加上这段配置,明确告诉DBT这是个增量模型,而且不需要唯一键:
{{ config( materialized='incremental', unique_key=none, # 关键:声明不需要唯一键 on_schema_change='ignore' # 这里可以根据你的需求改,比如要自动加新列就用'append_new_columns',怕结构变出错就用'fail' ) }}
2. 写核心的逻辑:先删后插
DBT的is_incremental()宏会帮我们判断是第一次全量运行,还是后续的增量运行。我们就利用这个宏来区分逻辑:
第一步:先定义要插入的新数据
先把你用来生成目标数据的查询逻辑包在CTE里,这里要注意保留用来判断日期的字段:
WITH new_segmentation_data AS ( -- 这里替换成你实际的取数+处理逻辑 -- 比如从源表拉取、做聚合、清洗等操作,最后要包含TRANSACTION_DATETIME和TLZ_MS_FECHA字段 SELECT *, -- 提前算出用来判断的日期,后面删和插都能用 COALESCE(TRANSACTION_DATETIME::date, TLZ_MS_FECHA::date) AS record_date FROM your_source_table -- 增量运行时,只取要处理的日期范围的数据,提升性能 {% if is_incremental() %} WHERE COALESCE(TRANSACTION_DATETIME::date, TLZ_MS_FECHA::date) BETWEEN '{{ var('start_date') }}' AND '{{ var('end_date') }}' {% endif %} )
第二步:增量运行时执行删除
如果是后续的增量运行,先执行和你原来完全一致的删除逻辑,清理掉目标表中对应日期范围的旧数据:
{% if is_incremental() %} -- 完全复刻你原来的删除逻辑,用{{ this }}指代当前模型的表,不用硬编码库表名 DELETE FROM {{ this }} WHERE COALESCE(TRANSACTION_DATETIME::date, TLZ_MS_FECHA::date) BETWEEN '{{ var('start_date') }}' AND '{{ var('end_date') }}'; {% endif %}
第三步:插入新数据
最后把新生成的数据插入到目标表,全量运行时插所有数据,增量运行时只插当前日期范围的新数据:
-- 插入数据,如果record_date是临时字段就用EXCLUDE排除,不需要的话可以去掉 SELECT * EXCLUDE record_date FROM new_segmentation_data
3. 一些实用的小提示
- 参数传递更灵活:你原来用的
{{ params.db }}这类硬编码,在DBT里用{{ this }}就可以指代当前模型的完整库表路径,不用手动拼字符串。另外start_date和end_date建议用DBT的变量传递,运行时用dbt run --vars '{"start_date": "2024-05-01", "end_date": "2024-05-31"}'来传参,比硬写在SQL里方便多了。 - 动态日期更省心:如果是按天跑前一天的数据,完全不用手动传参,用DBT的日期函数自动生成:
{% set yesterday = dateadd('day', -1, current_date) %} -- 把之前的日期判断改成这个 WHERE COALESCE(TRANSACTION_DATETIME::date, TLZ_MS_FECHA::date) = '{{ yesterday }}' - 测试要谨慎:第一次运行前,建议先在测试环境用
dbt run --full-refresh做全量初始化,之后再跑增量。删除操作前可以先手动查一下目标表中对应日期的记录数,确认逻辑没问题再执行。
要是你在写取数逻辑或者参数配置的时候碰到具体问题,随时说细节,我再帮你调!




