使用复合键删除不在其中的SQL

使用复合键删除不在其中的SQL,sql,sql-server,sql-server-2012,Sql,Sql Server,Sql Server 2012,我有一个系统集成项目,需要从一个数据库到另一个数据库进行CRUD。不是特别复杂。然而,当涉及到删除目标中存在但源中不存在的行时,我遇到了一点麻烦。标准模式包括:左连接、不存在或不在。我选择了左边的连接。我的“Phone”表使用复合键、员工“Id”和电话类型:Work、Home、Mobile等。标准的left join将删除源中没有的任何目标电话号码。这把整张桌子都打扫干净了注意:我只更新自上次更新以来已更改的记录,而不是整个目标和源。因此,我编写了一个修复程序,我怀疑这是一个非常糟糕的SQL:

我有一个系统集成项目,需要从一个数据库到另一个数据库进行CRUD。不是特别复杂。然而,当涉及到删除目标中存在但源中不存在的行时,我遇到了一点麻烦。标准模式包括:左连接、不存在或不在。我选择了左边的连接。我的“Phone”表使用复合键、员工“Id”和电话类型:Work、Home、Mobile等。标准的left join将删除源中没有的任何目标电话号码。这把整张桌子都打扫干净了注意:我只更新自上次更新以来已更改的记录,而不是整个目标和源。因此,我编写了一个修复程序,我怀疑这是一个非常糟糕的SQL:

-- SOURCE
DECLARE @tmpPhones TABLE(Id varchar(8), PhoneType int, PhoneNumber varchar(30), PRIMARY KEY (Id, PhoneType))
INSERT into @tmpPhones values 
('TEST123',  1, '12345678'),
('TEST123',  2, '12345678'),
('TEST123',  3, '12345678')

-- TARGET
DECLARE@Phone TABLE( Id varchar(8), PhoneType int, PhoneNumber varchar(30), PRIMARY KEY (Id, PhoneType))
INSERT into @Phone values 
('TEST123',  1, '12345678'), <-- Exists in both, leave
('TEST123',  2, '12345678'), <-- Exists in both, leave 
('TEST123',  3, '12345678'), <-- Exists in both, leave
('TEST123',  4, '12345678'), <-- ONLY delete this one!
('TEST456', 2, '12345678'),  <-- Ignore this employee Id
('TEST456', 3, '12345678'),     ""
('TEST456', 4, '12345678')      ""

DELETE p 
FROM  @Phone p 
    LEFT JOIN @tmpPhones t 
    ON t.Id = p.Id AND t.PhoneType = p.PhoneType 
WHERE  t.Id IS NULL AND t.PhoneType IS NULL 
    AND p.Id IN (SELECT Id FROM @tmpPhones) <-- a sad hack? 
——源代码
声明@tmpPhones表(Id varchar(8),PhoneType int,PhoneNumber varchar(30),主键(Id,PhoneType))
在@tmpPhones值中插入
('TEST123',1,'12345678'),
('TEST123',2,'12345678'),
('TEST123',3','12345678')
--目标
DECLARE@Phone表(Id varchar(8),PhoneType int,PhoneNumber varchar(30),主键(Id,PhoneType))
插入@Phone值

('TEST123',1',12345678'),使用
存在

DELETE p 
FROM  @Phone p
where exists (select 1 from @tmpPhones where Id = p.Id) 
AND not exists (select 1 from @tmpPhones where PhoneType = p.PhoneType) 
编辑:使用
cte
删除

with todelete as (
   select id,phonetype from phone
   except
   select id,phonetype from tmpphones t
   where exists (select 1 from phone where id = t.id)
   )
delete from phone 
where exists (select 1 from todelete where phone.id = id and phone.phonetype = phonetype)

使用
存在

DELETE p 
FROM  @Phone p
where exists (select 1 from @tmpPhones where Id = p.Id) 
AND not exists (select 1 from @tmpPhones where PhoneType = p.PhoneType) 
编辑:使用
cte
删除

with todelete as (
   select id,phonetype from phone
   except
   select id,phonetype from tmpphones t
   where exists (select 1 from phone where id = t.id)
   )
delete from phone 
where exists (select 1 from todelete where phone.id = id and phone.phonetype = phonetype)

合并似乎可以正常工作-但是您仍然需要查看id是否在引用集中,我看不出一个干净的方法来解决这个问题

MERGE @Phone AS TGT
USING (
SELECT * FROM @tmpPhones
) AS SRC
ON TGT.ID=SRC.ID AND TGT.PHONETYPE=SRC.PHONETYPE 
WHEN NOT MATCHED BY SOURCE AND tgt.id IN (SELECT id FROM @tmpPhones) THEN DELETE;

合并似乎可以正常工作-但是您仍然需要查看id是否在引用集中,我看不出一个干净的方法来解决这个问题

MERGE @Phone AS TGT
USING (
SELECT * FROM @tmpPhones
) AS SRC
ON TGT.ID=SRC.ID AND TGT.PHONETYPE=SRC.PHONETYPE 
WHEN NOT MATCHED BY SOURCE AND tgt.id IN (SELECT id FROM @tmpPhones) THEN DELETE;

我认为有两个exists语句非常符合逻辑:正如您所描述的

DELETE p 
FROM @Phone p
WHERE EXISTS (SELECT 1 FROM @tmpPhone t WHERE t.id = p.id) AND
      NOT EXISTS (SELECT 1 FROM @tmpPhone t WHERE t.id = p.id AND t.PhoneType = p.PhoneType) ;

我认为有两个exists语句非常符合逻辑:正如您所描述的

DELETE p 
FROM @Phone p
WHERE EXISTS (SELECT 1 FROM @tmpPhone t WHERE t.id = p.id) AND
      NOT EXISTS (SELECT 1 FROM @tmpPhone t WHERE t.id = p.id AND t.PhoneType = p.PhoneType) ;

是的,SQL Server 2012.LEFT JOIN在此完全错误是的,SQL Server 2012.LEFT JOIN在此完全错误此示例代码中没有运行?这不会删除所有其他员工的电话号码吗?使用select进行检查将返回我们不希望删除的id=TEST456的id(如果集合中有多个员工id,则编辑将不起作用)。第一个id在我的示例数据中完全可以即时删除。不知道它是否比我的黑客更快,但它确实看起来更合法。非常感谢!这没有在示例代码中运行?这不会删除所有其他员工的电话号码吗?使用select进行检查将返回我们不希望删除的id=TEST456的id(如果集合中有多个员工id,则编辑将不起作用)。第一个id在我的示例数据中完全可以即时删除。不知道它是否比我的黑客更快,但它确实看起来更合法。非常感谢!是的,这个很好用。同上。非常感谢戈登。盯着一个问题看太久,你必须要有一双新的眼睛。是的,这非常有效。同上。非常感谢戈登。盯着一个问题看太久,你必须要求有新的眼光。