代表性面试主题

数据面试:如何用 DuckDB 1.4 MERGE 设计可靠增量更新?

数据困难
Offer.cc 编辑团队发布 更新

题干

每天把变更文件增量合并到 DuckDB 分析表时,如何避免重复更新、错误覆盖和部分失败?

题干与适用场景

团队用 DuckDB 读取 Parquet 变更文件,想在本地分析库中维护客户维表和历史价格。请说明何时使用 MERGE INTO,如何处理同一键多行、删除、SCD Type 2、重跑和失败恢复。

面试官考察点

考察你是否理解匹配条件、动作顺序、源数据唯一性和事务边界;能否把 SQL 便利性转成可重跑的数据管道,并设计质量、审计和回滚。

回答前需要澄清的问题

先问业务主键、事件时间与去重规则,是否需要保留历史版本;再确认源文件是否可能重复、迟到或包含删除,以及失败后能否重建目标表。不要把 MERGE 自动当作 CDC exactly-once。

30 秒回答

“我会先在 staging 中按业务键和事件时间去重,验证每个目标键最多一条可应用变更,再在事务中执行 MERGE。当前值表用匹配更新和未匹配插入,历史表用 SCD Type 2 关闭旧版本并插入新版本;删除和重跑都要有明确语义。每批记录输入快照、行数和校验结果,失败则回滚并从同一快照重试。”

分步骤深入解答

定义目标表语义

区分当前快照表与历史表。当前表追求最新状态;SCD Type 2 需要 valid_fromvalid_tois_current 和版本约束。

先构建可审计 staging

保留源文件名、批次 ID、读取时间和行哈希。按业务键、事件时间和来源优先级去重,拒绝无法决定胜负的并列记录。

设计匹配与动作

匹配条件只使用稳定业务键,不把可变属性放进 ON。明确 WHEN MATCHED 的更新条件、WHEN NOT MATCHED 的插入和 WHEN NOT MATCHED BY SOURCE 的删除策略,避免误删迟到数据。

处理 SCD Type 2

对发生变化的当前行先设置结束时间,再插入新版本;相同内容不产生新版本。用唯一约束或质量查询保证同一键只有一个当前版本。

保证重跑与事务

批次 ID 和输入快照让任务可重跑。MERGE 与审计写入应在同一事务;失败后不提交部分结果,重试使用相同 staging,而不是重新读取可能变化的文件。

监控与回滚

比较源/目标插入、更新、删除计数,检查孤儿键、重复当前行和时间逆序。保留变更前快照或可重建路径,发现异常时按批次回滚。

高质量示范回答

我会把 Parquet 文件先落入带批次元数据的 staging,按业务键和事件时间去重并做行数、空值和删除比例校验。当前表用稳定键匹配更新/插入;历史表对变化行关闭旧版本并插入新版本,保证一个键只有一个 current。MERGE、审计和批次状态放在同一事务,失败用同一快照重跑。监控每批差异计数和重复版本,异常按批次回滚或重建。

常见错误

直接对原始文件 MERGE

源数据重复会让结果不确定或重复更新;应先 staging、去重和质量门禁。

把可变字段放入匹配条件

客户改名后会被当作新键;匹配应使用稳定业务键。

SCD Type 2 只做 INSERT

旧版本不会关闭,查询会得到多个 current;必须维护有效期和唯一约束。

重跑时重新读取源文件

文件可能已替换或新增,导致不同结果;应冻结输入快照与批次 ID。

追问及应对

同一键在源中出现两条不同事件怎么办?

按明确的事件时间、版本或来源优先级选一条;无法判定就隔离到 quarantine,不静默覆盖。

如何处理迟到删除?

依据删除事件时间与目标版本判断是否生效,记录 tombstone,并防止旧删除覆盖更新数据。

MERGE 失败后如何确认没有部分提交?

把 MERGE、审计和批次状态放在事务中,失败检查目标计数与批次状态,再用同一 staging 重试。

什么时候不用 MERGE?

若批次接近全量替换、匹配逻辑复杂或需要跨系统事务,可先生成新表并原子切换,降低逐行动作的不确定性。

公开来源

同类题目