[英]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.