簡體   English   中英

DELETE查詢非常慢

[英]Very slow DELETE query

我有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秒才能執行。

執行計划也顯示了一些我不理解的東西。 也許這就解釋了為什么它如此緩慢: 查詢兩個查詢

我用10,000個連續的整數填充了兩個列表,因此兩個列表都包含值1-10.000作為起始點。

正如您所看到的,兩個查詢顯示@ IdList2 實際行數為50.005.000 !!。 @ IdList1是正確的( 實際行數是10.000)

我知道還有其他解決方案如何解決這個問題。 就像填寫從第一個列表中刪除的第三個列表一樣。 但我的問題是:

為什么這些刪除查詢這么慢,為什么我會看到這些奇怪的查詢計划?

向表變量添加主鍵並觀察它們的尖叫聲

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對值的順序。

SQL Server在表變量為空時編譯計划,並且在添加行時不重新編譯它。 嘗試

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

這將考慮表變量中包含的實際行數並刪除嵌套循環計划

當然,通過約束在Id上創建索引也可能對使用表變量的其他查詢有益。

表變量中的表可以有主鍵,因此如果您的數據支持這些Id的唯一性,您可以通過

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

可能的解決方案:

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);

您正在使用Table Variables ,要么將主鍵添加到Table Variables ,要么將它們更改為Temporary Tables並添加INDEX 這將帶來更多的性能。 根據經驗,如果表只是小的,請使用TABLE Variables ,但是如果表正在擴展並包含大量數據,則使用臨時表。

我很想嘗試

DECLARE @IdList3 TABLE(Id INT);

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

不需要刪除。

嘗試這種替代語法:

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

編輯.....................

嘗試使用帶有索引的#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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM