MERGE 与 Upsert 的定位
维表与状态表要的不是流水,而是主键的最新态:同一个客户、同一个门店只保留一行。MERGE 用一条语句同时表达「匹配则更新、不匹配则插入」,比先删后插更安全,也更容易写成可重复执行的任务。不同引擎的语法接近,但事务能力、分桶要求和并发限制差别很大。
基本信息
| 适用场景 | 维表拉链、状态表最新态、幂等重跑 |
|---|---|
| 核心语法 | MERGE INTO 目标 USING 源 ON 条件 WHEN MATCHED / NOT MATCHED |
| 支撑引擎 | Hive ACID、Doris Unique Key、StarRocks 主键模型、Iceberg、Delta |
| 前置条件 | 目标表需支持行级更新(事务表或主键模型) |
| 风险点 | 全表扫描成本、并发写同分区 |
典型使用场景
- 维表更新:客户、门店、商品属性变化后只保留最新版本
- CDC 落仓:上游变更日志按主键合并到目标表
- 幂等重跑:任务失败重跑时结果与首次执行一致,不必先清空目标表
- 状态回填:订单状态、物流状态按最新事件覆盖
- 分区内更新:限定 dt 分区先过滤,再按主键合并,控制扫描范围
MERGE 写法示例
MERGE 写法示例
-- 目标表按主键合并,仅当前批次数据参与匹配
MERGE INTO dim_customer AS t
USING (SELECT * FROM stg_customer WHERE etl_batch = '20261012_01') AS s
ON t.customer_id = s.customer_id
WHEN MATCHED AND s.update_time > t.update_time THEN UPDATE SET
t.customer_name = s.customer_name,
t.update_time = s.update_time -- 用严格大于避免同批次反复更新
WHEN NOT MATCHED THEN INSERT (customer_id, customer_name, update_time)
VALUES (s.customer_id, s.customer_name, s.update_time);
-- 执行前先确认目标表类型与分桶/主键配置各引擎实现对照
| 引擎或表类型 | 实现方式 | 注意事项 |
|---|---|---|
| Hive ACID 事务表 | MERGE / UPDATE / DELETE | 需 ORC + 分桶 + transactional 属性 |
| Doris 主键模型 | 导入时按主键覆盖 | 需 Unique Key 模型与合适的 bucket |
| StarRocks 主键模型 | 导入按主键更新 | 主键索引带来写入开销 |
| Iceberg | MERGE INTO(v2 行级更新) | 小文件需定期合并 |
| 传统 Hive 外部表 | 不支持 | 只能重写整个分区 |
MERGE 的匹配条件决定幂等性:用 update_time 严格大于可以避免同一批次重复更新,用大于等于会让每次重跑都产生一次无效写入,累积出大量历史版本。
MERGE 在大分区表上会扫描整个匹配范围,成本远高于普通写入。能按 dt 先过滤就先过滤,把 MERGE 的范围压在当天分区内。