如何在MERGE INTO语句中使用JOIN?

soa*_*gem 5 sql t-sql sql-server merge sql-server-2008

我有三张桌子正在处理注册表格:[月],[成员]和[months_members].最后一个只是一个连接表.他们看起来像这样:

[months]
========
[id] INT (Primary Key)
[name] VARCHAR(50)
[date] DATE

[members]
=========
[id] INT (Primary Key)
[fname] VARCHAR(50)
[lname] VARCHAR(50)
[email] VARCHAR(300)

[months_members]
================
[id] INT (Primary Key)
[month_id] INT
[member_id] INT
Run Code Online (Sandbox Code Playgroud)

所以,从概念上来说非常简单.有一个成员列表,他们可以注册几个月.我拥有的表单允许他们注册不同的月份,实际上让管理员可以同时输入(或更改!)所有注册.所以我希望使用MERGE语句通过单个查询将数据导入数据库,而不是迭代多个查询.

所以这就是我开始编写的内容(即使我知道这不是正确的语法):

MERGE INTO [months_members] mm
INNER JOIN [months] mo ON mo.[id] = mm.[month_id] 
USING (
    SELECT 1 AS [month_id], 1 AS [member_id] UNION ALL
    SELECT 2 AS [month_id], 3 AS [member_id] UNION ALL
    SELECT 4 AS [month_id], 4 AS [member_id]
) AS u ON mm.[month_id] = u.[month_id] AND mm.[member_id] = u.[member_id] 
WHEN NOT MATCHED THEN 
    INSERT ([month_id], [member_id]) VALUES (u.[month_id], u.[member_id]) 
WHEN NOT MATCHED BY SOURCE AND DATEPART(YEAR, mo.[start]) = 2013 THEN 
    DELETE;
Run Code Online (Sandbox Code Playgroud)

希望这说明了我遇到的问题.我想将USING子查询中的任何新数据插入[months_members]表中.而且我还想删除子查询中存在的[months_members]表中的任何内容- 但仅当它对应于当前年份中的一个月时.如果数据在不存在对应于不同的子查询一年,我希望删除.我想单独保留这些历史数据.所以条件是踢球者.USINGUSINGDATEPART

所以我插入一张表,[months_members]但我也需要它知道[months]表.有没有办法实现这个目标?或者我是否必须承认并迭代一些SQL查询?

usr*_*usr 7

您可以使用CTE代替目标表(或视图).SQL Server可以通过CTE /视图跟踪更新.这是一个黑客攻击的演示:

SELECT *
INTO #temp
FROM sys.objects
GO

;WITH cte AS (
    SELECT t1.*
    FROM #temp t1
    JOIN #temp t2 ON t1.object_id = t2.object_id
)
MERGE cte USING (
    SELECT 1 AS [month_id], 1 AS [member_id] UNION ALL
    SELECT 2 AS [month_id], 3 AS [member_id] UNION ALL
    SELECT 4 AS [month_id], 4 AS [member_id]
) AS u ON cte.object_id = u.[month_id]
WHEN MATCHED THEN DELETE;
Run Code Online (Sandbox Code Playgroud)