我一直在尝试编写一个存储过程,在其中我可以使用具有以下条件的 Merge 执行 UpSert
这是我在 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
是为了做(在同一个子句中更新和插入)。为此,您可以使用 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')
存储过程更新
您可以使用
OUTPUT
末尾有MERGE
让它返回目标记录的修改记录,并包含$action
,您还将了解它是插入、更新还是删除。但是,结果集来自
MERGE
/OUTPUT
不能直接连接到 SOURCE 表,所以你可以做你的 INSERT
因为你只能取回 TARGET 记录。您不能使用 OUTPUT
的结果在来自 SOURCE 表的相关子查询中。最简单的方法是使用临时表或表变量来捕获输出。-- 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 记录,然后您就可以做您未完成的工作 INSERT
通过仅过滤属于 MERGE
的 SOURCE 记录更新。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 中条件匹配时更新和插入,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38328940/