题干与适用场景
团队用 DuckDB 读取 Parquet 变更文件,想在本地分析库中维护客户维表和历史价格。请说明何时使用 MERGE INTO,如何处理同一键多行、删除、SCD Type 2、重跑和失败恢复。
面试官考察点
考察你是否理解匹配条件、动作顺序、源数据唯一性和事务边界;能否把 SQL 便利性转成可重跑的数据管道,并设计质量、审计和回滚。
回答前需要澄清的问题
先问业务主键、事件时间与去重规则,是否需要保留历史版本;再确认源文件是否可能重复、迟到或包含删除,以及失败后能否重建目标表。不要把 MERGE 自动当作 CDC exactly-once。
30 秒回答
“我会先在 staging 中按业务键和事件时间去重,验证每个目标键最多一条可应用变更,再在事务中执行 MERGE。当前值表用匹配更新和未匹配插入,历史表用 SCD Type 2 关闭旧版本并插入新版本;删除和重跑都要有明确语义。每批记录输入快照、行数和校验结果,失败则回滚并从同一快照重试。”
分步骤深入解答
定义目标表语义
区分当前快照表与历史表。当前表追求最新状态;SCD Type 2 需要 validfrom、validto、is_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?
若批次接近全量替换、匹配逻辑复杂或需要跨系统事务,可先生成新表并原子切换,降低逐行动作的不确定性。