sql-server – 在TSQL-Merge中匹配条件时更新和插入

我一直在尝试编写一个存储过程,我可以使用Merge和以下条件执行UpSert

>如果记录存在,则将目标的EndDate更改为昨天的日期,即当前日期 – 1
>如果Record不存在,则插入新记录

这是我在SP中使用的表tblEmployee

CREATE TABLE tblEmployee
(
    [EmployeeID] [int] IDENTITY(1,1) NOT NULL, 
    [Name] [varchar](10) NOT NULL,  
    [StartDate] [date] NOT NULL,
    [EndDate] [date] NOT NULL
)

这是我的SP,它将UDTT作为输入参数

CREATE PROCEDURE [dbo].[usp_UpsertEmployees]
@typeEmployee typeEmployee READONLY -- It has same column like tblEmployye except EmployeeID
AS
BEGIN
    SET NOCOUNT ON;      

    MERGE INTO tblEmployee AS TARGET
    USING @typeEmployee AS SOURCE
    ON TARGET.Name = SOURCE.Name 

    WHEN MATCHED and TARGET.StartDate < SOURCE.StartDate
    THEN 

            --First Update Existing Record EndDate to Previous Date as shown below 
            UPDATE 
            set TARGET.EndDate = DATEADD(day, -1, convert(date, SOURCE.StartDate))

            -- Now Insert New Record 
            --INSERT VALUES(SOURCE.Name, SOURCE.StartDate, SOURCE.EndDate);

    WHEN NOT MATCHED by TARGET 
    THEN
            INSERT VALUES(SOURCE.Name, SOURCE.StartDate, SOURCE.EndDate);

    SET NOCOUNT OFF;        
END

如何在匹配列时执行更新现有记录和添加新记录

请有人在TSQL中解释合并的执行流程,即,

    WHEN MATCHED --Will this Execute Everytime

    WHEN NOT MATCHED by TARGET -- Will this Execute Everytime

    WHEN NOT MATCHED by SOURCE -- Will this Execute Everytime

是否所有以上3个条件都在Merge中每次都执行或者每次只执行匹配条件

提前致谢

最佳答案
这不是MERGE的意图(在同一条款中更新和插入).要实现此目的,您可以使用OUTPUT子句仅获取所有更新的记录. MERGE / OUTPUT组合非常挑剔.您的OUTPUT更新实际上是已更新的TARGET记录,因此您必须在temp / table变量中启动TARGET记录.然后你将那些背对着SOURCE进行匹配来进行INSERT.您将不被允许直接将输出结果连接回源,甚至可以在WHERE中用作相关子查询.

设置一些示例数据

下面的代码只是设置一些示例数据.

-- Setup sample data
DECLARE @typeEmployee TABLE (
    [Name] [varchar](10) NOT NULL,  
    [StartDate] [date] NOT NULL,
    [EndDate] [date] NOT NULL
)
DECLARE @tblEmployee TABLE (
    [EmployeeID] [int] IDENTITY(1,1) NOT NULL, 
    [Name] [varchar](10) NOT NULL,  
    [StartDate] [date] NOT NULL,
    [EndDate] [date] NOT NULL   
)
INSERT @tblEmployee VALUES ('Emp A', '1/1/2016', '2/1/2016')
INSERT @typeEmployee VALUES ('Emp A', '1/5/2016', '2/2/2016'), ('Emp B', '3/1/2016', '4/1/2016')

存储过程的更新

您可以在MERGE结尾处使用OUTPUT使其返回目标记录的已修改记录,并通过包含$action,您还将获得它是插入,更新还是删除.

但是,MERGE / OUTPUT的结果集不能直接与SOURCE表连接,因此您可以执行INSERT,因为您只能获取TARGET记录.您也不能在SOURCE表的相关子查询中使用OUTPUT的结果.最简单的方法是使用临时表或表变量来捕获输出.

-- Logic to do upsert
DECLARE @Updates TABLE (
    [Name] [varchar](10) NOT NULL,  
    [StartDate] [date] NOT NULL,
    [EndDate] [date] NOT NULL
)

INSERT @Updates
    SELECT
        Name,
        StartDate,
        EndDate
    FROM (
        MERGE INTO @tblEmployee AS TARGET
        USING @typeEmployee AS SOURCE
            ON TARGET.Name = SOURCE.Name 
        WHEN MATCHED AND TARGET.StartDate < SOURCE.StartDate
        THEN
            --First Update Existing Record EndDate to Previous Date as shown below 
            UPDATE SET
                EndDate = DATEADD(DAY, -1, CONVERT(DATE, SOURCE.StartDate))
        WHEN NOT MATCHED BY TARGET -- OR MATCHED AND TARGET.StartDate >= SOURCE.StartDate -- Handle this case?
        THEN
            INSERT VALUES(SOURCE.Name, SOURCE.StartDate, SOURCE.EndDate)
        OUTPUT $action, INSERTED.Name, INSERTED.StartDate, INSERTED.EndDate
        -- Use the MERGE to return all changed records of target table
    ) AllChanges (ActionType, Name, StartDate, EndDate)
    WHERE AllChanges.ActionType = 'UPDATE' -- Only get records that were updated

现在您已捕获MERGE的输出并过滤为仅获取更新的TARGET记录,然后您可以通过仅过滤属于MERGE更新的SOURCE记录来执行未完成的INSERT.

INSERT @tblEmployee
    SELECT
        SOURCE.Name,
        SOURCE.StartDate,
        SOURCE.EndDate
    FROM @typeEmployee SOURCE
    WHERE EXISTS (
        SELECT *
        FROM @Updates Updates
        WHERE Updates.Name = SOURCE.Name
            -- Other join conditions to ensure 1:1 match against SOURCE (start date?)
    )

输出继电器

这是更改后样本记录的输出.您进行了预期的TARGET更改.

-- Show output
SELECT * FROM @tblEmployee

转载注明原文:sql-server – 在TSQL-Merge中匹配条件时更新和插入 - 代码日志