14

Tôi gặp sự cố với hiệu suất SQL. Vì lý do đột ngột, các truy vấn sau đây rất chậm:Truy vấn DELETE rất chậm

Tôi có hai danh sách chứa Id của một bảng nhất định. Tôi cần xóa tất cả các bản ghi khỏi danh sách đầu tiên nếu Id đã tồn tại trong danh sách thứ hai:

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) 

Có thể có hai danh sách chứa hơn 10.000 hồ sơ. Trong trường hợp đó, cả hai truy vấn đều mất hơn 20 giây để thực thi.

Kế hoạch thực hiện cũng cho thấy điều tôi không hiểu. Có lẽ điều đó giải thích tại sao nó quá chậm: Queryplan of both queries

Tôi điền cả hai danh sách với 10.000 số nguyên tuần tự sao cho cả hai danh sách chứa giá trị 1-10.000 làm điểm bắt đầu.

Như bạn có thể thấy cả hai truy vấn hiển thị cho @ IdList2 Số hàng thực tế là 50.005.000 !!. @ IdList1 là chính xác (Số thực tế của hàng là 10.000)

Tôi biết còn có các giải pháp khác để giải quyết vấn đề này. Giống như điền vào danh sách thứ ba được loại bỏ khỏi danh sách đầu tiên. Nhưng câu hỏi của tôi là:

Tại sao các truy vấn xóa này quá chậm và tại sao tôi lại thấy các kế hoạch truy vấn lạ này?

+0

Đây có phải là một vấn đề có thể gặp phải trong kịch bản thế giới thực hay, chỉ trong tình huống cụ thể này? – Jodrell

+1

@Jodrell - Các vấn đề cơ bản của không có số liệu thống kê dựa trên biên dịch lại cho các biến bảng (và thiếu chỉ số hữu ích trên chúng) là rất phổ biến. –

Trả lời

14

Thêm một chìa khóa chính để biến bảng của bạn và xem chúng hét lên

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

vì không có chỉ mục trên các biến bảng, bất kỳ gia nhập hoặc truy vấn con phải kiểm tra về trình tự của 10.000 lần 10.000 = 100.000.000 cặp giá trị.

+0

Nó có giúp chỉ mục trên '@ IdList1' không? – Jodrell

+2

"Mọi tham gia hoặc truy vấn con phải kiểm tra theo thứ tự 10.000 lần 10.000 = 100.000.000 cặp giá trị." điều này chỉ đúng đối với các vòng lặp lồng nhau. Một băm hoặc hợp nhất tham gia sẽ xử lý mỗi đầu vào một lần (mặc dù một hợp nhất tham gia cũng sẽ cần một loại) –

+1

@martin, tôi đã không đọc những thứ đó trong một thời gian, vì vậy tôi đã quên các quy tắc, nhưng nó không chọn lồng nhau vòng vì không có chỉ mục? Để thực hiện các thuật toán lặp khác không cần chỉ mục để sắp xếp các giá trị? Ngoài ra, không có chỉ mục, nó vẫn phải kiểm tra mọi cặp giá trị - bất kể thuật toán lặp mà nó sử dụng để tạo ra chúng. - ngoại lệ là, như bạn lưu ý, một hợp nhất tham gia, nhưng ở đó nó phải presort chúng. –

12

SQL Server biên dịch kế hoạch khi biến bảng trống và không biên dịch lại khi hàng được thêm vào. Hãy thử

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

này sẽ đưa tài khoản của con số thực tế của hàng chứa trong biến bảng và thoát khỏi các vòng lồng nhau lên kế hoạch

Tất nhiên tạo một chỉ mục trên Id qua một hạn chế cũng có thể có lợi cho các truy vấn khác sử dụng biến bảng.

+0

Điều này mới đối với tôi. Bạn có thể làm rõ - Việc biên dịch ban đầu cacheplan sẽ xảy ra khi câu lệnh Delete gặp phải, đúng không? Không phải khi các biến bảng được khai báo? Tôi có nghĩa là, kế hoạch được biên dịch là cho Xóa, không phải cho khai báo biến bảng ... Nếu vậy, thì tại thời điểm đó sẽ không biến bảng được dân cư? Ngoài ra, nếu bạn không nhớ, bạn có thể cung cấp một tài liệu tham khảo? Tôi muốn đọc về điều này. –

+2

@CharlesBretana - Có một số liên kết và mã ví dụ trong [câu trả lời của tôi ở đây] (http://dba.stackexchange.com/questions/16385/whats-the-difference-between-a-temp-table-and-table- biến-in-sql-server) –

+0

cảm ơn ... Đã học được điều gì đó ngay hôm nay! –

2

Các bảng trong bảng biến có thể có khóa chính, vì vậy nếu dữ liệu của bạn hỗ trợ tính độc đáo cho các Id s, bạn có thể cải thiện hiệu suất bằng cách cho

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

Bạn đang sử dụng Table Variables, hoặc thêm một khóa chính của bảng hoặc thay đổi chúng thành Temporary Tables và thêm INDEX. Điều này sẽ dẫn đến hiệu suất nhiều hơn nữa. Theo quy tắc chung, nếu bảng chỉ nhỏ, hãy sử dụng TABLE Variables, tuy nhiên nếu bảng đang mở rộng và chứa nhiều dữ liệu thì hãy sử dụng bảng tạm thời.

-1

Hãy thử cú pháp thay thế này:

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

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

Hãy thử sử dụng #temp bảng có chỉ mục thay thế.

Đây là ví dụ chung về "DepartmentKey" là PK và 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 
+0

Thật không may, điều này cũng chậm. Cùng một kết quả và chính xác cùng một kế hoạch truy vấn. – hwcverwe

+0

Bạn buộc phải sử dụng @ biến-bảng, hoặc bạn có thể thử #temp bảng? – granadaCoder

+0

Nếu bạn có thể sử dụng #temp tables, hãy thử ví dụ trong phản hồi của tôi. – granadaCoder

2

giải pháp có thể:

1) Cố gắng tạo chỉ số như vậy

1,1) Nếu Danh sách {1 | 2} .id cột có giá trị duy nhất sau đó bạn có thể định nghĩa một nhóm chỉ số duy nhất sử dụng một hạn chế PK như thế này:

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

1,2) Nếu Danh sách {1 | 2} .id cột có thể có giá trị nhân bản thì bạn có thể xác định một nhóm chỉ số duy nhất sử dụng một hạn chế PK sử dụng một hình nộm IDENTITY cột như thế này:

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) Cố gắng thêm HASH JOIN truy vấn gợi ý như thế này:

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

tôi bị cám dỗ để thử

DECLARE @IdList3 TABLE(Id INT); 

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

Không xóa được yêu cầu.

+0

Nhưng nếu OP * cần * để xóa, như anh/cô ấy nói: 'Tôi cần xóa tất cả các bản ghi khỏi danh sách đầu tiên nếu Id đã tồn tại trong danh sách thứ hai' – oleksii

+0

@oleksii true, OP cho biết ví dụ của nó là một ví dụ liên quan đến hai biến bảng đó và xóa cụ thể. Tuy nhiên, điều này vẫn có thể hữu ích cho người đọc khác. – Jodrell

Các vấn đề liên quan