MsSql

关注公众号 jb51net

关闭
首页 > 数据库 > MsSql > SQL MERGE语句

SQLServer中MERGE语句的使用

作者:zxrhhm

MERGE语句用于根据两个表之间的条件来插入、更新或删除记录,本文主要介绍了SQLServer中MERGE语句的使用,具有一定的参考价值,感兴趣的可以了解一下

在 SQL Server 中,MERGE 语句用于根据两个表之间的条件来插入、更新或删除记录。它通常用于同步两个表的数据,其中一个表是源表(包含要插入或更新的数据),另一个是目标表(数据要插入或更新的表)。

1、本文内容

适用于:

根据与源表联接的结果,对目标表进行插入、更新或删除操作。 例如,根据与另一个表的区别,在一个表中插入、更新或删除行,从而同步两个表。

2、语法

SQL Server 和 Azure SQL 数据库的语法:

[ WITH <common_table_expression> [,...n] ]
MERGE
    [ TOP ( expression ) [ PERCENT ] ]
    [ INTO ] <target_table> [ WITH ( <merge_hint> ) ] [ [ AS ] table_alias ]
    USING <table_source> [ [ AS ] table_alias ]
    ON <merge_search_condition>
    [ WHEN MATCHED [ AND <clause_search_condition> ]
        THEN <merge_matched> ] [ ...n ]
    [ WHEN NOT MATCHED [ BY TARGET ] [ AND <clause_search_condition> ]
        THEN <merge_not_matched> ]
    [ WHEN NOT MATCHED BY SOURCE [ AND <clause_search_condition> ]
        THEN <merge_matched> ] [ ...n ]
    [ <output_clause> ]
    [ OPTION ( <query_hint> [ ,...n ] ) ]
;

<target_table> ::=
{
    [ database_name . schema_name . | schema_name . ] [ [ AS ] target_table ]
    | @variable [ [ AS ] target_table ]
    | common_table_expression_name [ [ AS ] target_table ]
}

<merge_hint>::=
{
    { [ <table_hint_limited> [ ,...n ] ]
    [ [ , ] { INDEX ( index_val [ ,...n ] ) | INDEX = index_val }]
    }
}

<merge_search_condition> ::=
    <search_condition>

<merge_matched>::=
    { UPDATE SET <set_clause> | DELETE }

<merge_not_matched>::=
{
    INSERT [ ( column_list ) ]
        { VALUES ( values_list )
        | DEFAULT VALUES }
}

<clause_search_condition> ::=
    <search_condition>

3、参数

4、备注

必须指定三个 MATCHED 子句中的至少一个子句,但可以按任何顺序指定。 无法在同一个 MATCHED 子句中多次更新一个变量。

MERGE 语句对目标表指定的任何插入、更新或删除操作受限于,在此语句中定义的任何约束,包括任何级联引用完整性约束。 如果 IGNORE_DUP_KEY 对目标表中的任何唯一索引都设置为 ON,MERGE 便会忽略此设置。

MERGE 语句需要一个分号 (😉 作为语句终止符。 如果运行没有终止符的 MERGE 语句,将引发错误 10713。

如果在 MERGE 之后使用,@@ROWCOUNT (Transact-SQL) 会返回为客户端插入、更新和删除的行的总数。

在数据库兼容级别设置为 100 或更高时,MERGE 为完全保留的关键字。 MERGE 语句可用于设置为 90 和 100 的数据库兼容性级别;不过,当数据库兼容性级别设置为 90 时,关键字不是完全保留的。

5、触发器的实现

对于在 MERGE 语句中指定的每个插入、更新或删除操作,SQL Server 都会触发对目标表定义的任何对应 AFTER 触发器,但不保证哪个操作最先或最后触发触发器。 为相同操作定义的触发器会遵循您指定的顺序进行触发。 有关设置触发器激发顺序的详细信息,请参阅指定第一个和最后一个触发器。

如果目标表已针对 MERGE 语句完成的插入、更新或删除操作启用了对自己定义的 INSTEAD OF 触发器,它必须已针对 MERGE 语句中指定的所有操作启用了 INSTEAD OF 触发器。

如果对 target_table 定义了任何 INSTEAD OF UPDATE 或 INSTEAD OF DELETE 触发器,则不会运行更新或删除操作。 而是会触发触发器,并相应地填充 inserted 和 deleted 表。

如果对 target_table 定义了任何 INSTEAD OF INSERT 触发器,则不会执行插入操作。 而是相应地填充表。

备注与单独的 INSERT 、 UPDATE 和 DELETE 语句不同,触发器内部由 @@ROWCOUNT 反映的行数可能更高。 任何 AFTER 触发器(无论触发器捕获的数据修改语句如何)内的 @@ROWCOUNT 都将反映受 MERGE 影响的总行数。 例如,如果 MERGE 语句插入一行、更新一行并删除一行,则任何 AFTER 触发器的 @@ROWCOUNT 都为 3,即使触发器仅为 INSERT 语句声明。

6、权限

需要对源表的 SELECT 权限和对目标表的 INSERT、UPDATE 或 DELETE 权限。 有关详细信息,请参阅 SELECT、INSERT、UPDATE 和 DELETE 文章中的“权限”部分。

7、有关索引的最佳做法

通过使用 MERGE 语句,可以使用单个语句替换各个 DML 语句。 由于操作是在单个语句中执行的,因此可以提高查询性能,从而最大限度地减少处理源表和目标表中数据的次数。 然而,性能的提升取决于是否进行了正确的索引和联接以及是否遵守了其他注意事项。

若要提高 MERGE 语句的性能,我们建议您遵循以下索引准则:

8、MERGE 的并发注意事项

在锁定方面,MERGE 不同于离散的、连续 INSERT、UPDATE 和 DELETE 语句。 MERGE 仍执行 INSERT、UPDATE 和 DELETE 操作,但使用的是不同的锁定机制。 为满足某些应用程序的需要,编写离散的 INSERT、UPDATE 和 DELETE 语句可能更高效。 MERGE 可能会大规模引入复杂的并发问题或需要高级故障排除。 因此,计划在部署到生产环境之前全面测试任何 MERGE 语句。

MERGE 语句非常适合在以下(但不限于)场景中替代离散 INSERT、UPDATE 和 DELETE 操作:

并发的其他注意事项:

https://learn.microsoft.com/en-us/sql/t-sql/statements/set-transaction-isolation-level-transact-sql?view=sql-server-ver16

测量和诊断 MERGE 性能以下功能可帮助您测量和诊断 MERGE 语句的性能。

9、示例

9.1、借助派生的源表,使用 MERGE 对目标表执行 UPDATE 和 INSERT 操作

下面的示例使用 MERGE 以更新或插入行的方式来修改 数据库中的 T_UnitMeasure表。

CREATE TABLE T_UnitMeasure(UnitMeasureCode  NCHAR(8),NameInfo NVARCHAR(25));
INSERT INTO T_UnitMeasure VALUES(N'Car00001',N'问界M5'),(N'Car00004',N'问界M9');
SELECT * FROM T_UnitMeasure;

在这里插入图片描述

当源表中的 NewCode 值与目标表T_UnitMeasure 的(UnitMeasureCode ) 列中的值匹配时,就会更新此目标表中的 NameInfo 列。 如果 NewCode 的值不匹配,就会将源行插入目标表中。 此源表是一个派生表,它使用 Transact-SQL 表值构造函数指定源表的多个行。 有关在派生表中使用表值构造函数的详细信息,请参阅表值构造函数 (Transact-SQL)。

OUTPUT 子句可用于查询 MERGE 语句的结果,有关详细信息,请参阅 OUTPUT 子句。 下面的示例还展示了如何在表变量中存储 OUTPUT 子句的结果。 然后,通过运行返回已插入行数和已更新行数的简单选择操作,汇总 MERGE 语句的结果。

DECLARE @SummaryOfChanges TABLE(Change VARCHAR(20));  
  
MERGE INTO T_UnitMeasure AS Target  
USING (VALUES (N'Car00001',N'问界M9'), (N'Car00002',N'坦克700'), (N'Car00003',N'奔驰大G500'))  
       AS Source (NewCode, NewNameInfo)  
ON Target.UnitMeasureCode = Source.NewCode  
WHEN MATCHED THEN  
  UPDATE SET NameInfo = Source.NewNameInfo  
WHEN NOT MATCHED BY TARGET THEN  
  INSERT (UnitMeasureCode, NameInfo) VALUES (NewCode, NewNameInfo)  
OUTPUT $action INTO @SummaryOfChanges;  
  
-- Query the results of the table variable.  
SELECT Change, COUNT(*) AS CountPerChange  
FROM @SummaryOfChanges  
GROUP BY Change;

在这里插入图片描述

在这里插入图片描述

查询目标表T_UnitMeasure返回的结果复合预期,Car00001=问界M5更新为“问界M9”,其他进行了插入操作。

9.2、使用 MERGE 在一个语句中对表执行 INSERT 和 UPDATE 操作

常见方案是,更新表中的一个或多个列(若有匹配行)。 或者,在没有匹配行的情况下,将数据作为新行插入。 处理两种方案之一的一般方法是,将参数传递给包含相应 UPDATE 和 INSERT 语句的存储过程。 借助 MERGE 语句,可以在一个语句中同时执行这两项任务。 下面的示例显示了数据库中一个同时包含 INSERT 语句和 UPDATE 语句的存储过程。 随后,过程被修改为,使用一个 MERGE 语句运行等效的操作。

CREATE PROCEDURE dbo.InsertUnitMeasure(@UnitMeasureCode NCHAR(8), @NameInfo NVARCHAR(25))
AS
BEGIN
    SET NOCOUNT ON;

    -- Update the row if it exists.
    UPDATE T_UnitMeasure
    SET NameInfo = @NameInfo
    WHERE UnitMeasureCode = @UnitMeasureCode

    -- Insert the row if the UPDATE statement failed.
    IF (@@ROWCOUNT = 0)
    BEGIN
        INSERT INTO T_UnitMeasure (UnitMeasureCode,NameInfo) VALUES (@UnitMeasureCode, @NameInfo)
    END
END;
GO
-- Test the procedure and return the results.
EXEC InsertUnitMeasure @UnitMeasureCode = 'Car00003', @NameInfo = N'奔驰E300L';

SELECT * FROM T_UnitMeasure;

UnitMeasureCode NameInfo
--------------- -------------------------
Car00001        问界M9
Car00004        问界M9
Car00002        坦克700
Car00003        奔驰E300L

(4 行受影响)

-- Rewrite the procedure to perform the same operations using the
-- MERGE statement.
-- Create a temporary table to hold the updated or inserted values
-- from the OUTPUT clause.
CREATE TABLE #MyTempTable (
    ExistingCode NCHAR(8),
    ExistingName NVARCHAR(50),
    ActionTaken NVARCHAR(10),
    NewCode NCHAR(8),
    NewName NVARCHAR(50),
);
GO

DROP TABLE #MyTempTable

ALTER PROCEDURE dbo.InsertUnitMeasure (@UnitMeasureCode NCHAR(8), @NameInfo NVARCHAR(25))
AS
BEGIN
    SET NOCOUNT ON;

    MERGE T_UnitMeasure AS tgt
    USING (SELECT @UnitMeasureCode, @NameInfo) AS src (UnitMeasureCode, NameInfo)
        ON (tgt.UnitMeasureCode = src.UnitMeasureCode)
    WHEN MATCHED THEN
      UPDATE SET NameInfo = src.NameInfo
    WHEN NOT MATCHED BY TARGET THEN
      INSERT (UnitMeasureCode,NameInfo) VALUES (@UnitMeasureCode, @NameInfo)
    OUTPUT deleted.*,
        $action,
        inserted.*
    INTO #MyTempTable;
END;
GO
-- Test the procedure and return the results.
EXEC InsertUnitMeasure @UnitMeasureCode = 'Car00001', @NameInfo = N'问界M7';
EXEC InsertUnitMeasure @UnitMeasureCode = 'Car00002', @NameInfo = N'丰田皇冠';
EXEC InsertUnitMeasure @UnitMeasureCode = 'Car00005', @NameInfo = N'宝马X5';


SELECT * FROM #MyTempTable;

ExistingCode ExistingName       ActionTaken NewCode        NewName
------------ ------------------ --------    -----------  ---------- 
Car00001     问界M9               UPDATE      Car00001 		问界M7
Car00002     坦克700              UPDATE      Car00002 		丰田皇冠
NULL         NULL                INSERT      Car00005 		宝马X5
(3 行受影响)

以上操作完成执行成功后,查询结果如下:

SELECT * FROM T_UnitMeasure;

UnitMeasureCode NameInfo
--------------- -------------------------
Car00001        问界M7
Car00004        问界M9
Car00002        丰田皇冠
Car00003        奔驰E300L
Car00005        宝马X5

(5 行受影响)

9.3、使用 MERGE 在一个语句中对表执行 UPDATE 和 DELETE 操作

下面的示例使用 MERGE 根据 T_UnitMeasure 表中记录。 通过merge 更新 或删除 T_UnitMeasure 表数据。

SELECT * FROM T_UnitMeasure

UnitMeasureCode NameInfo
--------------- -------------------------
Car00001        问界M7
Car00004        问界M9
Car00002        丰田皇冠
Car00003        奔驰E300L
Car00005        宝马X5

(5 行受影响)
-- 更新及删除
MERGE T_UnitMeasure AS Target
USING (VALUES (N'Car00001',N'问界M5'), (N'Car00004',N'问界M9'))  
       AS Source (NewCode, NewNameInfo)  
ON Target.UnitMeasureCode = Source.NewCode  
WHEN MATCHED AND Target.NameInfo=Source.NewNameInfo THEN
  DELETE
WHEN MATCHED THEN
  UPDATE SET NameInfo = Source.NewNameInfo
OUTPUT $action,
    Inserted.UnitMeasureCode,
    Inserted.NameInfo,
    Deleted.UnitMeasureCode,
    Deleted.NameInfo;

$action    UnitMeasureCode NameInfo                  UnitMeasureCode NameInfo
---------- --------------- ------------------------- --------------- -------------------------
UPDATE     Car00001        问界M5                      Car00001        问界M7
DELETE     NULL            NULL                      Car00004        问界M9

(2 行受影响)

SELECT * FROM T_UnitMeasure

UnitMeasureCode NameInfo
--------------- -------------------------
Car00001        问界M5
Car00002        丰田皇冠
Car00003        奔驰E300L
Car00005        宝马X5

(4 行受影响)

9.4、将 MERGE 语句的执行结果插入到另一个表中

CREATE TABLE T_UnitMeasure_list 
( ID BIGINT IDENTITY(1,1) PRIMARY KEY, 
  UnitMeasureCode  NCHAR(8),
  NameInfo NVARCHAR(25),
  Action NVARCHAR(16),
  UnitMeasureCode_old NCHAR(8),
  NameInfo_old  NVARCHAR(25),
);
GO

INSERT INTO T_UnitMeasure_list
SELECT UnitMeasureCode,NameInfo,Action,UnitMeasureCode_old,NameInfo_old
FROM (
    MERGE T_UnitMeasure AS pi
    USING (VALUES (N'Car00005',N'宝马X5'), (N'Car00001',N'问界M9'))  AS src (NewCode, NewNameInfo) 
        ON pi.UnitMeasureCode = src.NewCode
    WHEN MATCHED AND pi.NameInfo=src.NewNameInfo THEN 
      DELETE
	WHEN MATCHED THEN
	  UPDATE SET pi.NameInfo = src.NewNameInfo
    OUTPUT $action,
        Inserted.UnitMeasureCode,
        Inserted.NameInfo,
        Deleted.UnitMeasureCode AS UnitMeasureCode_old,
        Deleted.NameInfo AS NameInfo_old
    ) AS Changes(Action, UnitMeasureCode,NameInfo, UnitMeasureCode_old, NameInfo_old)
;
GO

SELECT * FROM T_UnitMeasure_list;

ID                   UnitMeasureCode NameInfo                  Action           UnitMeasureCode_old NameInfo_old
-------------------- --------------- ------------------------- ---------------- ------------------- -------------------------
1                    Car00001        问界M9                      UPDATE           Car00001            问界M5
2                    NULL            NULL                        DELETE           Car00005            宝马X5

(2 行受影响)

SELECT * FROM T_UnitMeasure;

UnitMeasureCode NameInfo
--------------- -------------------------
Car00001        问界M9
Car00002        丰田皇冠
Car00003        奔驰E300L

(3 行受影响)

9.5、使用 MERGE 对图形数据库中的目标边缘表执行 INSERT 或 UPDATE 操作

在此示例中,创建节点表 Person 和 City 以及边缘表 livesIn。 如果 Person 和 City 之间尚不存在 livesIn 边缘,则对边缘使用 MERGE 语句,并插入新行。 如果已有边缘,只需更新 livesIn 边缘上的 StreetAddress 属性。

-- CREATE node and edge tables
CREATE TABLE Person
(
    ID INTEGER PRIMARY KEY,
    PersonName VARCHAR(100)
)
AS NODE
GO

CREATE TABLE City
(
    ID INTEGER PRIMARY KEY,
    CityName VARCHAR(100),
    StateName VARCHAR(100)
)
AS NODE
GO

CREATE TABLE livesIn
(
    StreetAddress VARCHAR(100)
)
AS EDGE
GO

-- INSERT some test data into node and edge tables
INSERT INTO Person VALUES (1, 'Ron'), (2, 'David'), (3, 'Nancy')
GO

INSERT INTO City VALUES (1, 'Redmond', 'Washington'), (2, 'Seattle', 'Washington')
GO

INSERT livesIn SELECT P.$node_id, C.$node_id, c
FROM Person P, City C, (values (1,1, '123 Avenue'), (2,2,'Main Street')) v(a,b,c)
WHERE P.id = a AND C.id = b
GO

-- Use MERGE to update/insert edge data
CREATE OR ALTER PROCEDURE mergeEdge
    @PersonId integer,
    @CityId integer,
    @StreetAddress varchar(100)
AS
BEGIN
    MERGE livesIn
        USING ((SELECT @PersonId, @CityId, @StreetAddress) AS T (PersonId, CityId, StreetAddress)
                JOIN Person ON T.PersonId = Person.ID
                JOIN City ON T.CityId = City.ID)
        ON MATCH (Person-(livesIn)->City)
    WHEN MATCHED THEN
        UPDATE SET StreetAddress = @StreetAddress
    WHEN NOT MATCHED THEN
        INSERT ($from_id, $to_id, StreetAddress)
        VALUES (Person.$node_id, City.$node_id, @StreetAddress) ;
END
GO

-- Following will insert a new edge in the livesIn edge table
EXEC mergeEdge 3, 2, '4444th Avenue'
GO

-- Following will update the StreetAddress on the edge that connects Ron to Redmond
EXEC mergeEdge 1, 1, '321 Avenue'
GO

-- Verify that all the address were added/updated correctly
SELECT PersonName, CityName, StreetAddress
FROM Person , City , livesIn
WHERE MATCH(Person-(livesIn)->city)
GO

在这里插入图片描述

10、相关内容

到此这篇关于SQLServer中MERGE语句的使用的文章就介绍到这了,更多相关SQL MERGE语句内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家! 

您可能感兴趣的文章:
阅读全文