MERGE INTO 是什么?
MERGE INTO(在 Oracle 中)也称为 UPSERT,它将 INSERT、UPDATE 和 DELETE 操作合并到一个语句中。
基本语法结构:
MERGE INTO 目标表
USING 源数据
ON (连接条件)
WHEN MATCHED THEN -- 当匹配时(记录存在)
UPDATE SET ... -- 执行更新
[DELETE WHERE ...] -- 可选:删除条件
WHEN NOT MATCHED THEN -- 当不匹配时(记录不存在)
INSERT (...) -- 执行插入
VALUES (...);
类比理解
想象成两个 Excel 表格的操作:
| 步骤 | 类比 Excel 操作 |
|---|---|
| MERGE INTO | 目标表格(要更新的表) |
| USING | 源表格(提供数据的表) |
| ON | 用哪一列匹配两个表格 |
| WHEN MATCHED | 如果找到相同的行 |
| WHEN NOT MATCHED | 如果没找到相同的行 |
MERGE 关键要点总结:
-
MERGE INTO+ 目标表 -
USING+ 源数据(表或子查询) -
ON+ 匹配条件(像 JOIN) -
WHEN MATCHED+ 更新操作 -
WHEN NOT MATCHED+ 插入操作(可选)
为什么用 MERGE 而不是 UPDATE?
使用 MERGE 的优势:
-
一次操作,多种处理:可以同时处理存在和不存在的情况
-
性能更好:避免先查询再更新的两次操作
-
原子性:要么全部成功,要么全部失败
-
避免重复代码
MERGE INTO 使用案例
案例1:最简单的 MERGE - 员工薪资同步
sql
-- 场景:用新员工表同步更新员工表
MERGE INTO employees target
USING new_employees source
ON (target.emp_id = source.emp_id)
WHEN MATCHED THEN
UPDATE SET
target.salary = source.salary,
target.department = source.department
WHEN NOT MATCHED THEN
INSERT (emp_id, name, salary, department)
VALUES (source.emp_id, source.name, source.salary, source.department);
案例2:商品价格同步 - 根据来源更新
sql
-- 场景:根据不同供应商更新商品价格
MERGE INTO product_prices target
USING (
-- 多个来源的价格数据
SELECT product_id, supplier_id, price, 'SUPPLIER_A' as source
FROM supplier_a_prices
UNION ALL
SELECT product_id, supplier_id, price, 'SUPPLIER_B' as source
FROM supplier_b_prices
) source
ON (target.product_id = source.product_id AND target.supplier_id = source.supplier_id)
WHEN MATCHED THEN
UPDATE SET
target.price = source.price,
target.source = source.source,
target.update_time = SYSDATE
WHEN NOT MATCHED THEN
INSERT (product_id, supplier_id, price, source, create_time)
VALUES (source.product_id, source.supplier_id, source.price, source.source, SYSDATE);
案例3:会员积分更新 - 含条件更新和删除
sql
-- 场景:更新会员积分,过期积分删除
MERGE INTO member_points target
USING (
SELECT member_id, SUM(points) as earn_points
FROM point_records
WHERE earn_date >= TRUNC(SYSDATE) - 30 -- 最近30天
GROUP BY member_id
) source
ON (target.member_id = source.member_id)
WHEN MATCHED THEN
UPDATE SET
target.total_points = target.total_points + source.earn_points,
target.last_earn_date = SYSDATE
DELETE WHERE target.expire_date < SYSDATE -- 删除过期记录
WHEN NOT MATCHED THEN
INSERT (member_id, total_points, last_earn_date, expire_date)
VALUES (source.member_id, source.earn_points, SYSDATE, ADD_MONTHS(SYSDATE, 12));
案例4:订单状态同步 - 简化的实际业务场景
sql
-- 场景:根据物流单更新订单状态
MERGE INTO orders target
USING logistics source
ON (target.order_no = source.order_no)
WHEN MATCHED THEN
UPDATE SET
target.status =
CASE
WHEN source.delivery_status = 'DELIVERED' THEN 'COMPLETED'
WHEN source.delivery_status = 'SHIPPED' THEN 'IN_TRANSIT'
ELSE target.status
END,
target.delivery_time =
CASE
WHEN source.delivery_status = 'DELIVERED' THEN source.delivery_time
ELSE target.delivery_time
END;
案例5:你的业务场景简化版
sql
-- 简化你的追踪明细更新
MERGE INTO ct_price_tracking_item target
USING (
-- 计算接收数量
SELECT item_id, SUM(receive_qty) as total_received
FROM receive_records
WHERE receive_date >= DATE '2024-01-01'
GROUP BY item_id
) source
ON (target.item_id = source.item_id)
WHEN MATCHED THEN
UPDATE SET
target.receive_qty = source.total_received,
target.update_time = SYSDATE;
练习案例:创建测试表和数据
sql
-- 1. 创建目标表
CREATE TABLE products (
product_id NUMBER PRIMARY KEY,
product_name VARCHAR2(50),
price NUMBER(10,2),
stock_qty NUMBER,
update_date DATE
);
-- 2. 创建源表
CREATE TABLE price_updates (
product_id NUMBER,
new_price NUMBER(10,2),
update_type VARCHAR2(10)
);
-- 3. 插入测试数据
INSERT INTO products VALUES (1, 'Product A', 100, 50, SYSDATE);
INSERT INTO products VALUES (2, 'Product B', 200, 30, SYSDATE);
INSERT INTO price_updates VALUES (1, 120, 'INCREASE');
INSERT INTO price_updates VALUES (3, 300, 'NEW');
-- 4. 执行 MERGE(练习用)
MERGE INTO products target
USING price_updates source
ON (target.product_id = source.product_id)
WHEN MATCHED THEN
UPDATE SET
target.price = source.new_price,
target.update_date = SYSDATE
WHEN NOT MATCHED THEN
INSERT (product_id, product_name, price, stock_qty, update_date)
VALUES (source.product_id, 'New Product', source.new_price, 0, SYSDATE);