SQL Server 2008 Merge语句实战:实现数据的增删改同步

拾光
拾光
发布时间:2026-10-15 | 2人浏览过
原创

SQL Server 2008提供了强大的Merge语句,允许开发者在一次操作中根据源表与目标表的比较结果,同步执行插入、更新和删除操作,从而高效地保持两个表之间的数据一致性。

Merge语句的核心功能

Merge语句的主要用途是整合对目标表的多种修改动作。它通过联接源表和目标表,识别两者的差异:

  • Matched:源表中存在且目标表中也有对应行时,可执行更新。
  • Not Matched By Target:源表中存在但目标表中没有的行,可执行插入。
  • Not Matched By Source:目标表中存在但源表中没有的行,可执行删除。

这种机制常用于将分支节点(如分店)的数据变更同步回中心节点(如总库),或反之。

准备工作:构建测试环境

为了清晰展示Merge的效果,我们首先创建结构相同的“总产品表”和“分店产品表”,并初始化基础数据。

第1步:创建总产品表Demo_AllProducts

目的:建立需要同步的中心表,包含初始的5条产品记录。

代码如下:
-- 如果表存在则删除
if OBJECT_ID('Demo_AllProducts') is not null drop table Demo_AllProducts go 
-- 创建表结构
Create table Demo_AllProducts (
    PKID int not null identity(1,1) primary key ,
    DName Nvarchar(20) null,
    DCode NVarchar(30) null,
    DDate datetime null
)
go 
-- 插入初始数据 (SQL Server 2008支持多值INSERT)
Insert into Demo_AllProducts (DName,DCode,DDate) values 
('DemoA','AAA',GETDATE()), 
('DemoB','BBB',GETDATE()), 
('DemoC','CCC',GETDATE()), 
('DemoD','DDD',GETDATE()), 
('DemoE','EEE',GETDATE()); 

select * from Demo_AllProducts;

第2步:创建分店产品表Demo_Shop1_Product

目的:建立作为数据源的分支表,其中部分产品编码与总表重合(如AAA, CCC),但名称不同;同时新增了总表中不存在的产品(FFF)。

代码如下:
-- 创建分店表
if OBJECT_ID('Demo_Shop1_Product') is not null drop table Demo_Shop1_Product go 
Create table Demo_Shop1_Product (
    PKID int not null identity(1,1) primary key ,
    DName Nvarchar(20) null,
    DCode NVarchar(30) null,
    DDate datetime null
)
go 
-- 插入分店数据
Insert into Demo_Shop1_Product (DName,DCode,DDate) values 
('DemoA','AAA',GETDATE()), 
('DemoB','CCC',GETDATE()), -- 注意:这里代码写错了示例意图,通常应该是对应另一个编码,假设意在修改现有或新增。
                          -- 原文示例中DemoB对应CCC,而原总表BBB对应DemoB,这里假设分店有AAA(同名), CCC(原名DemoB), FFF(新)
                          -- 修正逻辑以符合下文解释:分店有 'DemoA/AAA', 'DemoB/CCC' (可能是笔误,按原文执行), 'DemoF/FFF'
                          -- 为严谨起见,以下代码严格按原文输入:
('DemoB','CCC',GETDATE()), 
('DemoF','FFF',GETDATE()); 

select * from Demo_Shop1_Product;

此时,总表有A,B,C,D,E五个编码;分店表有AAA, CCC, FFF三个记录。我们要以字段为依据进行同步。

执行数据同步操作

第3步:完整同步(含更新、插入和删除)

目的:将分店数据完全合并到总表。若编码相同且名称不同,更新名称;若总表无该编码,则插入;若分店无该编码,则从总表删除该行。

代码如下:
Merge Into Demo_AllProducts p -- 目标表别名p
using Demo_Shop1_Product s on p.DCode=s.DCode -- 关联条件
-- 当匹配时,如果名称不同则更新
When Matched and P.DName<>s.DName Then Update set P.DName=s.DName 
-- 当目标表中不存在(即分店有,总表没有)时插入
When Not Matched By Target Then Insert (DName,DCode,DDate) values (s.DName,s.DCode,s.DDate) 
-- 当源表中不存在(即总表有,分店没有)时删除
When Not Matched By Source Then Delete;

执行结果分析:执行完成后,总表仅保留与分店匹配的三项(AAA, CCC, FFF)。原总表中的B(BBB), C(CCC-此处因代码逻辑DCode一致被更新或删除,取决于前序状态), D, E均被移除,因为分店中没有对应的AAA/CCC/FFF以外的编码记录。最终总表内容与分店表保持一致。

第4步:仅同步新增与更新(不删除)

目的:在实际业务中,往往只需要汇总分店的增量数据到总部,而不希望删除总部原有但在分店下架的商品。此时省略Delete子句。

代码如下:
Merge Into Demo_AllProducts p 
using Demo_Shop1_Product s on p.DCode=s.DCode 
When Matched and P.DName<>s.DName Then Update set P.DName=s.DName 
When Not Matched By Target Then Insert (DName,DCode,DDate) values (s.DName,s.DCode,s.DDate);

执行后结果:

  • DemoA (AAA): 名称未变,保留。
  • DemoB (BBB): 总表原有,分店无,未触发删除,保留。
  • DemoB (CCC): 分店有此记录(DemoB/CCC),若总表曾有同编码且名称不同则更新。注:原文示例逻辑中,分店有CCC码,总表原无CCC码(原为BBB),故触发插入?或者原文意指分店表中有一条记录其DCode为CCC。让我们细看:总表有CCC吗?有,Total表里有C/DemoC/CCC。分店有CCC吗?有,Shop表里有DemoB/CCC。因此Matched,且P.DName(DemoC) != S.DName(DemoB),所以更新Total表的C名为DemoB。
  • DemoF (FFF): 总表无,触发插入。
  • DemoD, DemoE: 总表有,分店无,不触发删除,保留。

因此,总表最终包含:AAA(不变), B(原BBB不变), C(改名DemoB), D(不变), E(不变), F(新增)。

监控Merge语句的影响

在执行Merge时,了解受影响的行数和具体变更详情非常重要。

第5步:获取影响行数

可以使用系统变量获取本次Merge影响的总行数:

  • @@ROWCOUNT:返回int类型影响行数。
  • ROWCOUNT_BIG():返回bigint类型,适用于超过20亿行的超大操作。

第6步:输出详细变更日志

使用OUTPUT子句可以将Deleted(旧值)、Inserted(新值)或两者存入临时表或表变量中,便于审计。

代码如下:
-- 定义表变量存储输出日志
Declare @tableVarRecord Table (
    MPKID int not null identity(1,1) primary key ,
    PKID int null ,
    DName Nvarchar(20) null ,
    DCode NVarchar(30) null ,
    DDate datetime null )

Merge Into Demo_AllProducts p 
using Demo_Shop1_Product s on p.DCode=s.DCode 
When Matched and P.DName<>s.DName Then Update set P.DName=s.DName 
When Not Matched By Target Then Insert (DName,DCode,DDate) values (s.DName,s.DCode,s.DDate) 
When Not Matched By Source Then Delete OUTPUT deleted.* INTO @tableVarRecord;

-- 查询影响总数
select @@ROWCOUNT as Count1, ROWCOUNT_BIG() as Count2 
-- 查看被删除的记录详情
select * from @tableVarRecord;

结果说明: @tableVarRecord中将包含所有被Delete操作清除的行详情(即分店表里没有的总表记录)。这在需要撤销操作或生成报表时非常有用。

总结

Merge语句是SQL Server 2008及更高版本中处理“Upsert”(更新或插入)操作的利器,它能显著减少网络往返次数和锁竞争,提高数据同步效率。通过合理组合Matched、Not Matched By Target和Not Matched By Source三种状态,您可以灵活控制数据的同步策略,并利用Output子句确保数据变更的可追溯性。

以上就是SQL Server 2008中Merge语句用法的详细内容,更多关于SQL Server数据同步的资料请关注本站其它相关文章!

相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表