在where子句中使用临时表

时间:2009-07-28 11:44:41

标签: sql sql-server sql-server-2005

我想在某些(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。

4 个答案:

答案 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行受影响)