英文:
Merge when not matched by source without deleting other rows
问题
以下是您要翻译的部分:
我试图使用合并(merge)从源表更新目标表。源表中没有所有目标表的行,因此当我使用 WHEN NOT MATCHED BY SOURCE
时,所有不在我的源表中的目标行都会被删除。我只想删除特定的行。
所以,举个例子,我在我的源表中有这个:
Userid | skill_id | default_skill | |
---|---|---|---|
1132 | 2160 | 1 |
我的目标表有很多行,但这是一个例子:
Userid | skill_id | default_skill | |
---|---|---|---|
1132 | 421 | 0 | |
1132 | 2160 | 1 | |
1131 | 789 | 1 |
如果我简单地执行 WHEN NOT MATCHED BY SOURCE THEN Delete
,它将删除目标表中的 1131 用户,因为它不在源表中。我只想删除目标表中与源表不匹配的那一行。所以它将删除 |1132|421|0| 行。
我应该在用户ID上执行合并,然后当匹配且技能ID不匹配时进行删除吗?但是如果我需要匹配时它们匹配并且我只想更新 default_skill
列,我不能有多个 WHEN MATCH,对吗?
这是我目前尝试执行的合并:
英文:
I am trying to update a target table from a source table using merge. The source table does not have all the rows that the target table does, so when I do WHEN NOT MATCHED BY SOURCE
, all the target rows not in my source table get deleted. I only want to delete specific ones.
So for example I have this in my source table:
Userid | skill_id | default_skill | |
---|---|---|---|
1132 | 2160 | 1 |
My target table has many rows, but here is an example:
Userid | skill_id | default_skill | |
---|---|---|---|
1132 | 421 | 0 | |
1132 | 2160 | 1 | |
1131 | 789 | 1 |
If I simple do WHEN NOT MATCHED BY SOURCE THEN Delete
, it will delete 1131 userid from the target table because it is not in the source. I only want to delete the one from the target table that doesn't match the source. So it would delete the |1132|421|0| row.
Would I merge on the userid then when matched and the skill_ids don't match, delete? But what if I need a when matched but they do match and I just want to update the default_skill
column. I can't have multiple WHEN MATCH can I?
This is my current merge that I am trying to do it in:
MERGE appuser_skills USING
(
SELECT
userid,
#tmp.username,
s.value AS skill_id,
CASE
WHEN s.value = #tmp.primaryskillid THEN 1
ELSE 0
END AS default_skill
FROM
#tmp
CROSS APPLY
dbo.splitinteger(#tmp.skilllist,',') s
JOIN skill WITH(NOLOCK) ON skill.skill_id = s.value
) a
ON appuser_skills.user_id = a.userid AND appuser_skills.skill_id = a.skill_id
WHEN MATCHED THEN
UPDATE SET
default_skill = CASE
WHEN a.default_skill = 1 THEN 1
ELSE 0
END
WHEN NOT MATCHED THEN
INSERT
(
user_id,
skill_id,
weight,
proficiency,
default_skill
)
VALUES
(
(SELECT user_id FROM appuser WHERE appuser.username = a.username),
a.skill_id,
0,
0,
a.default_skill
);
答案1
得分: 1
-
WHEN MATCHED AND target.default_skill <> source.default_skill THEN
条件将检查匹配的行中源表和目标表的default_skill
值是否不匹配。在这种情况下,它会执行更新操作,将目标表中的default_skill
值设置为与源表匹配。 -
WHEN NOT MATCHED BY SOURCE THEN DELETE
条件保持不变:它将删除目标表中没有与源表匹配的行。
英文:
-
The
WHEN MATCHED AND target.default_skill <> source.default_skill THEN
condition will check if thedefault_skill
values do not match between the source and target tables for matched rows. In that case, it performs an update to set thedefault_skill
value in the target table to match the source table. -
The
WHEN NOT MATCHED BY SOURCE THEN DELETE
condition remains the same : it will delete rows from the target table that do not have a match in the source table.
MERGE appuser_skills AS target
USING
(
SELECT
userid,
s.value AS skill_id,
CASE
WHEN s.value = #tmp.primaryskillid THEN 1
ELSE 0
END AS default_skill
FROM
#tmp
CROSS APPLY dbo.splitinteger(#tmp.skilllist,',') s
JOIN skill WITH(NOLOCK) ON skill.skill_id = s.value
) AS source
ON (target.user_id = source.userid AND target.skill_id = source.skill_id)
WHEN MATCHED AND target.default_skill <> source.default_skill THEN
UPDATE SET target.default_skill = source.default_skill
WHEN NOT MATCHED BY SOURCE THEN
DELETE;
答案2
得分: 1
我注意到你也想要一个插入操作。我认为不是所有的情况都可以通过合并来解决。我认为现在是时候做你从一开始就应该做的事情了,不要使用合并,或者至少使用多个语句:
-- 创建测试数据
drop table #source
drop table #target
SELECt *
into #source
FROM (
VALUES (1132, 2160, 1)
, (1132, 666, 0)
) t (Userid,skill_id,default_skill)
select *
into #target
FROM (
VALUES (1132, 421, 0)
, (1132, 2160, 0)
, (1131, 789, 1)
) t (Userid,skill_id,default_skill)
-- 实际合并操作
;WITH target AS (
select *
from #target t -- 我们只处理源表中的用户
WHERE EXISTS(
SELECT 1
FROM #source s
WHERE s.Userid = t.UserId
)
)
merge target AS t
USING #source s
ON s.UserID = t.UserID
AND s.skill_id = t.skill_id
WHEN NOT MATCHED BY SOURCE THEN DELETE
WHEN NOT MATCHED BY TARGET THEN INSERT (UserId, skill_id, default_skill)
VALUES (s.UserId, s.skill_id, s.default_skill)
WHEN MATCHED and s.default_skill <> t.default_skill THEN UPDATE
SET default_skill = s.default_skill
output inserted.*, deleted.*, $action
;
-- 单独插入全新用户
insert into #target (
Userid, skill_id, default_skill
)
select Userid, skill_id, default_skill
FROm #source s
WHERE NOT EXISTS(
SELECT 1
FROM #target t
WHERE t.Userid = s.userId
--AND t.skill_id = s.skill_id -- 不应该需要
)
select *
from #source
select *
from #target
我选择将完全新用户的插入操作拆分出来,因为通常使用 WHERE NOT EXISTS
最容易检查。
对于删除操作,我确保参与合并的目标行只来自于 #source 表中的用户。
英文:
I noticed you also want an insert. I don't think all those can be solved by merge. I think it's time to do what you should've done from the beginning, don't use merge, or at least use several statements:
-- Create test data
drop table #source
drop table #target
SELECt *
into #source
FROM (
VALUES (1132, 2160, 1)
, (1132, 666, 0)
) t (Userid,skill_id,default_skill)
select *
into #target
FROM (
VALUES (1132, 421, 0)
, (1132, 2160, 0)
, (1131, 789, 1)
) t (Userid,skill_id,default_skill)
-- The actual merge
;WITH target AS (
select *
from #target t -- We work only with users in the source table
WHERE EXISTS(
SELECT 1
FROM #source s
WHERE s.Userid = t.UserId
)
)
merge target AS t
USING #source s
ON s.UserID = t.UserID
AND s.skill_id = t.skill_id
WHEN NOT MATCHED BY SOURCE THEN DELETE
WHEN NOT MATCHED BY TARGET THEN INSERT (UserId, skill_id, default_skill)
VALUES (s.UserId, s.skill_id, s.default_skill)
WHEN MATCHED and s.default_skill <> t.default_skill THEN UPDATE
SET default_skill = s.default_skill
output inserted.*, deleted.*, $action
;
-- Separate insert of completely new users
insert into #target (
Userid, skill_id, default_skill
)
select Userid, skill_id, default_skill
FROm #source s
WHERE NOT EXISTS(
SELECT 1
FROM #target t
WHERE t.Userid = s.userId
--AND t.skill_id = s.skill_id -- Shouldn't be needed
)
select *
from #source
select *
from #target
I choose to split out the INSERT of completely new users, since it's usually the easiest to check using WHERE NOT EXISTS.
For the delete, i make sure target rows participating in the merge only comes from users in the #source-table.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论