我想在某些(6)表中删除许多具有相同字段值的行。我可以通过删除每个表中的一个子查询的结果来做到这一点(解决方案1),这将是多余的,因为子查询每次都是相同的;所以我想将子查询的结果存储在临时表中,并删除表中每行(临时表)的值(解决方案2)。哪种解决方案更好?
第一个解决方案:
DELETE FROM dbo.SubProtocols
WHERE ProtocolID IN (
SELECT ProtocolID
FROM dbo.Protocols
WHERE WorkplaceID = @WorkplaceID
)
DELETE FROM dbo.ProtocolHeaders
WHERE ProtocolID IN (
SELECT ProtocolID
FROM dbo.Protocols
WHERE WorkplaceID = @WorkplaceID
)
// ...
DELETE FROM dbo.Protocols
WHERE WorkplaceID = @WorkplaceID
第二个解决方案:
DECLARE @Protocols table(ProtocolID int NOT NULL)
INSERT INTO @Protocols
SELECT ProtocolID
FROM dbo.Protocols
WHERE WorkplaceID = @WorkplaceID
DELETE FROM dbo.SubProtocols
WHERE ProtocolID IN (
SELECT ProtocolID
FROM @Protocols
)
DELETE FROM dbo.ProtocolHeaders
WHERE ProtocolID IN (
SELECT ProtocolID
FROM @Protocols
)
// ...
DELETE FROM dbo.Protocols
WHERE WorkplaceID = @WorkplaceID
是否可以在没有子查询的情况下执行解决方案2?说做WHERE ProtocolID IN @Protocols
(但语法正确)?
我使用的是Microsoft SQL Server 2005。
答案 0 :(得分:4)
虽然您可以通过连接避免SQL Server中的子查询,如下所示:
delete from sp
from subprotocols sp
inner join protocols p on
sp.protocolid = p.protocolid
and p.workspaceid = @workspaceid
你会发现,这并没有让你获得任何超过任何一种方法的表现。通常,使用子查询,SQL Server 2005会将in
优化为inner join
,因为它不依赖于每一行。此外,SQL Server可能会在您的情况下缓存子查询,因此将其推送到临时表中很可能是不必要的。
但是,第一种方式在交易期间容易受到Protocols
的变化的影响,而第二种方式则不然。只是想一想。
答案 1 :(得分:1)
可以试试这个
DELETE FROM dbo.ProtocolHeaders
FROM dbo.ProtocolHeaders INNER JOIN
dbo.Protocols ON ProtocolHeaders.ProtocolID = Protocols.ProtocolID
WHERE Protocols.WorkplaceID = @WorkplaceID
答案 2 :(得分:1)
DELETE ... FROM
是标准SQL DELETE
的T-SQL扩展,它提供了使用子查询的替代方法。来自帮助:
d。使用基于子查询的DELETE 并使用Transact-SQL扩展 以下示例显示了 用于删除的Transact-SQL扩展 来自基表的记录 基于连接或相关 子查询。第一个DELETE语句 显示与SQL-2003兼容的子查询 解决方案,第二个DELETE 语句显示Transact-SQL 延期。两个查询都删除了行 来自SalesPersonQuotaHistory表 基于存储的年初至今销售额 在SalesPerson表中。
-- SQL-2003 Standard subquery USE AdventureWorks; GO DELETE FROM Sales.SalesPersonQuotaHistory WHERE SalesPersonID IN (SELECT SalesPersonID FROM Sales.SalesPerson WHERE SalesYTD > 2500000.00); GO -- Transact-SQL extension USE AdventureWorks; GO DELETE FROM Sales.SalesPersonQuotaHistory FROM Sales.SalesPersonQuotaHistory AS spqh INNER JOIN Sales.SalesPerson AS sp ON spqh.SalesPersonID = sp.SalesPersonID WHERE sp.SalesYTD > 2500000.00; GO
在第二个解决方案中,你会想要像
这样的东西- 未经测试! 删除 dbo.SubProtocols - ProtocolHeaders等 从 dbo.SubProtocols INNER JOIN @Protocols ON SubProtocols.ProtocolID = @Protocols.ProtocolID
是否无法改变您的设计,以便所有的遗留协议表都有一个FOREIGN KEY
DELETE CASCADE
到主Protocols
表?那么你可以从DELETE
Protocols
得到FOREIGN KEY
,剩下的就会被照顾......
编辑添加:
如果您已经设置了DELETE CASCADE
,则需要使用DDL来更改它们(我认为需要删除并重新创建)才能让DELETE
处于启用状态。一旦到位,主表中的DELETE
将自动{{1}}来自子表的相关记录。
答案 3 :(得分:0)
如果没有临时表,则可能会在第二次删除时删除不同的行,但这需要执行三个操作。
您可以从第一个表中删除并使用 OUTPUT INTO 子句将所有ID插入临时表,然后使用该临时表删除第二个表。这将确保您只用两个语句删除相同的键。
declare @x table(RowID int identity(1,1) primary key, ValueData varchar(3))
declare @y table(RowID int identity(1,1) primary key, ValueData varchar(3))
declare @temp table (RowID int)
insert into @x values ('aaa')
insert into @x values ('bab')
insert into @x values ('aac')
insert into @x values ('bad')
insert into @x values ('aae')
insert into @x values ('baf')
insert into @x values ('aag')
insert into @y values ('aaa')
insert into @y values ('bab')
insert into @y values ('aac')
insert into @y values ('bad')
insert into @y values ('aae')
insert into @y values ('baf')
insert into @y values ('aag')
DELETE @x
OUTPUT DELETED.RowID
INTO @temp
WHERE ValueData like 'a%'
DELETE y
FROM @y y
INNER JOIN @temp t ON y.RowID=t.RowID
select * from @x
select * from @y
SELECT OUTPUT:
RowID ValueData
----------- ---------
2 bab
4 bad
6 baf
(3 row(s) affected)
RowID ValueData
----------- ---------
2 bab
4 bad
6 baf
(3行受影响)