批量Upsert指的是对一批数据执行操作时,若目标表中已存在匹配记录则更新对应字段,若不存在则插入新记录,在SQL Server中利用Merge语句是实现该需求的高效方案,相比循环逐行处理的方式,能显著减少语句执行次数和表扫描成本。

Merge语句基础语法
Merge语句的核心逻辑是将源数据集合与目标表进行匹配,根据匹配结果执行不同的操作,基础语法结构如下:
-- 目标表:需要被更新或插入数据的表
-- 源数据:需要处理的批量数据集合,可以是表、临时表、表变量或查询语句返回的结果集
MERGE INTO 目标表 AS target
USING 源数据 AS source
ON target.匹配字段 = source.匹配字段 -- 定义匹配条件,通常是主键或唯一键
WHEN MATCHED THEN -- 匹配成功时执行更新操作
UPDATE SET target.字段1 = source.字段1, target.字段2 = source.字段2
WHEN NOT MATCHED THEN -- 匹配失败时执行插入操作
INSERT (字段1, 字段2, 字段3) VALUES (source.字段1, source.字段2, source.字段3);
批量Upsert完整实现步骤
1. 准备测试环境
首先创建目标表用于演示,表中user_id为主键,作为匹配条件:
-- 创建用户目标表
CREATE TABLE user_info (
user_id INT PRIMARY KEY,
user_name NVARCHAR(50),
user_age INT,
update_time DATETIME DEFAULT GETDATE()
);
-- 插入测试初始数据
INSERT INTO user_info (user_id, user_name, user_age)
VALUES (1, '张三', 20), (2, '李四', 22);
2. 构造批量源数据
实际场景中源数据可能来自其他系统同步、文件导入等,这里用临时表模拟批量待处理数据:
-- 创建临时表存储批量待处理数据
CREATE TABLE #tmp_user (
user_id INT,
user_name NVARCHAR(50),
user_age INT
);
-- 插入待处理的批量数据,包含已存在和不存在的用户记录
INSERT INTO #tmp_user (user_id, user_name, user_age)
VALUES (1, '张三更新', 21), -- user_id=1已存在,需要更新
(2, '李四更新', 23), -- user_id=2已存在,需要更新
(3, '王五', 25); -- user_id=3不存在,需要插入
3. 使用Merge执行批量Upsert
通过Merge语句将临时表的数据批量合并到目标表:
MERGE INTO user_info AS target
USING #tmp_user AS source
ON target.user_id = source.user_id -- 以user_id作为匹配条件
WHEN MATCHED THEN -- 匹配到记录则更新用户名、年龄和更新时间
UPDATE SET target.user_name = source.user_name,
target.user_age = source.user_age,
target.update_time = GETDATE()
WHEN NOT MATCHED THEN -- 未匹配到记录则插入新数据
INSERT (user_id, user_name, user_age)
VALUES (source.user_id, source.user_name, source.user_age);
-- 查看执行后的结果
SELECT * FROM user_info;
4. 执行结果验证
执行上述语句后,目标表的数据会变为:
| user_id | user_name | user_age | update_time |
|---|---|---|---|
| 1 | 张三更新 | 21 | 执行Merge的时间 |
| 2 | 李四更新 | 23 | 执行Merge的时间 |
| 3 | 王五 | 25 | 执行Merge的时间 |
性能优化要点
- 匹配条件的字段需要建立索引,尤其是目标表的匹配字段,若没有索引会导致全表扫描,大批量数据时性能下降明显。
- 源数据集合尽量提前过滤重复数据,避免同一个源数据多次匹配到目标表记录,引发更新冲突。
- 若批量数据量极大,可以分批次执行Merge操作,每次处理1万到10万条数据,避免长时间锁表影响其他业务。
- 不需要执行的操作可以省略,比如只需要插入不存在的数据,可以只保留
WHEN NOT MATCHED分支,减少语句执行开销。
注意事项
Merge语句执行时默认开启事务,若执行过程中出现异常会自动回滚,也可以手动添加事务控制,确保批量操作的原子性:
BEGIN TRANSACTION;
BEGIN TRY
MERGE INTO user_info AS target
USING #tmp_user AS source
ON target.user_id = source.user_id
WHEN MATCHED THEN
UPDATE SET target.user_name = source.user_name,
target.user_age = source.user_age,
target.update_time = GETDATE()
WHEN NOT MATCHED THEN
INSERT (user_id, user_name, user_age)
VALUES (source.user_id, source.user_name, source.user_age);
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION;
-- 可以抛出错误信息
THROW;
END CATCH
使用Merge语句进行批量Upsert时,不需要循环执行单条插入或更新语句,单个语句即可完成全部逻辑,对于万级以上的批量数据,性能通常比传统方式提升数倍到数十倍,是SQL Server中处理批量合并操作的首选方案。
SQL_ServerMerge语句批量Upsert高性能修改时间:2026-06-30 03:36:34