订阅更新

SQL MERGE INTO 如何实现增量更新?

面试问 183 约 2 分钟读完

本文目录
  1. 面试怎么回答
  2. 先明确三类候选记录
  3. ON 条件必须代表真正的业务唯一键
  4. 更新不是把所有字段重新覆盖
  5. 删除语义需要单独设计
  6. 幂等要靠输入边界和校验闭环
  7. 一个更稳妥的模板
  8. 参考资料

面试怎么回答

我会先确认业务唯一键和输入是增量日志还是全量快照,再决定 MERGE 的三个分支。源表必须在业务键上去重,MATCHED 只更新允许变化且版本更新的字段,NOT MATCHED 插入;删除要依赖明确的删除事件或快照语义,不能因为本批没出现就删除。最后用插入、更新、删除计数和唯一性校验保证幂等。

先把这段讲顺;面试官追问时,再展开下面的原理和细节。

详细讲解

增量任务重跑后,目标表多了重复数据;补数时又把正常记录覆盖了。

这类问题通常不是 MERGE INTO 不够强,而是匹配键、源端去重和删除语义没有先设计清楚。

MERGE 的本质是先用业务键把源表与目标表分成“匹配、仅源端、仅目标端”三类,再为每一类定义可重复执行的动作。

先明确三类候选记录

执行 MERGE 时,系统先按 ON 条件连接源表和目标表。匹配到的记录进入 MATCHED,只在源端出现的记录进入 NOT MATCHED BY TARGET,部分引擎还支持只在目标端出现的 NOT MATCHED BY SOURCE。WHEN 分支按顺序判断,一条候选记录只执行一个动作。

ON 条件必须代表真正的业务唯一键

不能为了让语句跑通,随手用日期、状态等非唯一字段做匹配。若一条目标记录匹配到多条源记录,可能报基数冲突,也可能在不同引擎中产生不确定结果。生产里通常先用 ROW_NUMBER 按业务键去重,并明确保留最新版本还是优先级最高版本。

MERGE 的三类候选记录

更新不是把所有字段重新覆盖

更稳妥的做法是只更新发生变化且允许被源端改写的字段。审计时间、人工修正值、首次创建时间不应被无条件覆盖;空字符串和 NULL 也要先约定语义。否则一次迟到数据回放,就可能把目标表中的高质量值改回旧值。

删除语义需要单独设计

源端不再出现,并不天然等于目标记录应该删除。全量快照同步可以考虑 NOT MATCHED BY SOURCE 后软删;增量日志则不能这么做,因为“本批没来”只是没有变化。若源端有删除标记,优先按标记更新有效状态,并保留可追溯信息。

幂等增量的五道防线

幂等要靠输入边界和校验闭环

同一批数据重复执行,结果应保持一致。除了 MERGE 语句,还要固定批次范围、去重规则和版本比较条件。执行后至少核对源端唯一键数、插入数、更新数、删除数以及目标表唯一性,异常批次要能定位和回滚。

一个更稳妥的模板

先在 USING 子查询中完成过滤与去重,再以业务主键匹配;MATCHED 分支增加版本号或更新时间条件,避免旧数据覆盖新数据;NOT MATCHED 再插入。若引擎支持 RETURNING 或变更统计,把动作类型落到审计表,补数时更容易解释。

参考资料

刷题进度保存在当前浏览器