DELETE查询非常慢

时间:2013-05-23 12:59:55

标签: sql sql-server performance sql-execution-plan table-variable

我遇到SQL性能问题。出于突发原因,以下查询非常缓慢:

我有两个列表,其中包含某个表的Id。如果Id已经存在于第二个列表中,我需要删除第一个列表中的所有记录:

DECLARE @IdList1 TABLE(Id INT)
DECLARE @IdList2 TABLE(Id INT)

-- Approach 1
DELETE list1
FROM @IdList1 list1
INNER JOIN @IdList2 list2 ON list1.Id = list2.Id

-- Approach 2
DELETE FROM @IdList1
WHERE Id IN (SELECT Id FROM @IdList2)

两个列表可能包含超过10.000条记录。在这种情况下,两个查询都需要超过20秒才能执行。

执行计划也显示了一些我不明白的事情。也许这就解释了为什么它如此缓慢: Queryplan of both queries

我用10,000个连续整数填充两个列表,因此两个列表都包含值1-10.000作为起点。

如您所见,@ IdList2 实际行数的两个查询显示为50.005.000 !!。 @ IdList1是正确的(实际行数是10.000)

我知道还有其他解决方案如何解决这个问题。就像填写从第一个列表中删除的第三个列表一样。但我的问题是:

为什么这些删除查询的速度如此之慢?为什么我会看到这些奇怪的查询计划?

7 个答案:

答案 0 :(得分:15)

向表变量添加主键并观察它们的尖叫

DECLARE @IdList1 TABLE(Id INT primary Key not null)
DECLARE @IdList2 TABLE(Id INT primary Key not null)

因为这些表变量没有索引,所以任何连接或子查询都必须检查10,000次10,000 = 100,000,000对值的顺序。

答案 1 :(得分:12)

SQL Server在表变量为空时编译计划,并且在添加行时不重新编译它。尝试

DELETE FROM @IdList1
WHERE Id IN (SELECT Id FROM @IdList2)
OPTION (RECOMPILE)

这将考虑表变量中包含的实际行数并摆脱嵌套循环计划

当然,通过约束在Id上创建索引可能对使用表变量的其他查询也有好处。

答案 2 :(得分:2)

表变量中的表可以有主键,因此如果您的数据支持这些Id的唯一性,那么您可以通过

来提高性能
DECLARE @IdList1 TABLE(Id INT PRIMARY KEY)
DECLARE @IdList2 TABLE(Id INT PRIMARY KEY)

答案 3 :(得分:2)

可能的解决方案:

1)尝试创建索引

1.1)如果List {1 | 2} .Id列具有唯一值,那么您可以使用PK约束定义唯一的聚簇索引,如下所示:

DECLARE @IdList1 TABLE(Id INT PRIMARY KEY);
DECLARE @IdList2 TABLE(Id INT PRIMARY KEY);

1.2)如果List {1 | 2} .Id列可能具有重复值,那么您可以使用伪约束IDENTITY使用PK约束来定义唯一的聚簇索引:

DECLARE @IdList1 TABLE(Id INT, DummyID INT IDENTITY, PRIMARY KEY (ID, DummyID) );
DECLARE @IdList2 TABLE(Id INT, DummyID INT IDENTITY, PRIMARY KEY (ID, DummyID) );

2)尝试添加HASH JOIN查询提示,如下所示:

DELETE list1
FROM @IdList1 list1
INNER JOIN @IdList2 list2 ON list1.Id = list2.Id
OPTION (HASH JOIN);

答案 4 :(得分:1)

您正在使用Table Variables,要么在表格中添加主键,要么将其更改为Temporary Tables并添加INDEX。这将带来更多的性能。根据经验,如果表很小,请使用TABLE Variables,但是如果表正在扩展并包含大量数据,则使用临时表。

答案 5 :(得分:0)

我很想尝试

DECLARE @IdList3 TABLE(Id INT);

INSERT @IdList3
SELECT Id FROM @IDList1 ORDER BY Id
EXCEPT
SELECT Id FROM @IDList2 ORDER BY Id

不需要删除。

答案 6 :(得分:-1)

尝试这种替代语法:

DELETE deleteAlias
FROM @IdList1 deleteAlias
WHERE EXISTS (
        SELECT NULL
        FROM @IdList2 innerList2Alias
        WHERE innerList2Alias.id=deleteAlias.id
    )

EDIT .....................

尝试使用带索引的#temp表。

这是一个通用示例,其中" DepartmentKey"是PK和FK。

IF OBJECT_ID('tempdb..#Department') IS NOT NULL
begin
        drop table #Department
end


CREATE TABLE #Department 
( 
    DepartmentKey int , 
    DepartmentName  varchar(12)
)



CREATE INDEX IX_TEMPTABLE_Department_DepartmentKey ON #Department (DepartmentKey)




IF OBJECT_ID('tempdb..#Employee') IS NOT NULL
begin
        drop table #Employee
end


CREATE TABLE #Employee 
( 
    EmployeeKey int , 
    DepartmentKey int ,
    SSN  varchar(11)
)



CREATE INDEX IX_TEMPTABLE_Employee_DepartmentKey ON #Employee (DepartmentKey)


Delete deleteAlias 
from #Department deleteAlias
where exists ( select null from #Employee innerE where innerE.DepartmentKey = deleteAlias.DepartmentKey )





IF OBJECT_ID('tempdb..#Employee') IS NOT NULL
begin
        drop table #Employee
end

IF OBJECT_ID('tempdb..#Department') IS NOT NULL
begin
        drop table #Department
end