Truy vấn gói trong IF EXISTS làm cho nó rất chậm


16

Tôi có câu hỏi dưới đây:

select databasename 
from somedb.dbo.bigtable l where databasename ='someval' and source  <>'kt'
and not exists(select 1 from dbo.smalltable c where c.source=l.source)

Các truy vấn trên hoàn thành trong ba giây.

Nếu truy vấn trên trả về bất kỳ giá trị nào, chúng tôi muốn thủ tục được lưu trữ thành EXIT, vì vậy tôi viết lại như sau:

If Exists(
select databasename 
from somedb.dbo.bigtable l where databasename ='someval' and source  <>'kt'
and not exists(select 1 from dbo.smalltable c where c.source=l.source)
)
Begin
Raiserror('Source missing',16,1)
Return
End

Tuy nhiên, điều này mất 10 phút.

Tôi có thể viết lại truy vấn trên như bên dưới, cũng hoàn thành trong chưa đầy 3 giây:

  select databasename 
from somedb.dbo.bigtable l where databasename ='someval' and source  <>'kt'
and not exists(select 1 from dbo.smalltable c where c.source=l.source
if @@rowcount >0
Begin
Raiserror('Source missing',16,1)
Return
End

Vấn đề với viết lại ở trên là truy vấn trên là một phần của thủ tục được lưu trữ lớn hơn và nó trả về nhiều tập kết quả. Trong C #, chúng tôi lặp lại qua từng bộ kết quả và thực hiện một số xử lý.

Ở trên trả về một tập kết quả trống, vì vậy nếu tôi thực hiện theo phương pháp này, tôi phải thay đổi C # của mình và triển khai lại.

Vì vậy, câu hỏi của tôi là,

Tại sao sử dụng chỉ IF EXISTSthay đổi kế hoạch để mất rất nhiều thời gian?

Dưới đây là các chi tiết có thể giúp bạn và cho tôi biết nếu bạn cần bất kỳ chi tiết nào:

  1. Tạo bảng và tập lệnh thống kê để có cùng kế hoạch với tôi
  2. Kế hoạch thực hiện chậm
  3. Kế hoạch thực hiện nhanh

    Kế hoạch chậm sử dụng Brentozar Dán kế hoạch Kế hoạch
    nhanh bằng cách sử dụng Brentozar Dán kế hoạch

Lưu ý: Cả hai truy vấn đều giống nhau (sử dụng tham số), sự khác biệt duy nhất là EXISTS(tôi có thể đã mắc một số lỗi trong khi ẩn danh mặc dù).

Các kịch bản tạo bảng dưới đây:

http://pastebin.com/CgSHeqXc - số liệu thống kê bảng nhỏ
http://pastebin.com/GUu9KfpS - số liệu thống kê bảng lớn


Thảo luận về câu hỏi này đã được chuyển đến phòng chat này .
Paul White phục hồi Monica

Câu trả lời:


18

Như đã được giải thích bởi Paul trắng trong bài viết trên blog của mình: Bên trong Optimizer: Mục tiêu Row Trong Depth các EXISTSgiới thiệu một mục tiêu liên tiếp, mà thích NESTED LOOPShay MERGE JOINhơnHASH MATCH

Để làm ví dụ cuối cùng, hãy xem xét rằng một nửa tham gia hợp lý (chẳng hạn như truy vấn phụ được giới thiệu với EXISTS) chia sẻ chủ đề chung: cần nhanh chóng tối ưu hóa để tìm hàng khớp đầu tiên.

Trong truy vấn của bạn, điều này rõ ràng xảy ra để giới thiệu các vòng lặp lồng nhau và loại bỏ song song, dẫn đến một kế hoạch chậm hơn.

Vì vậy, có lẽ bạn sẽ cần tìm cách viết lại truy vấn của mình mà không cần sử dụng NOT EXISTStừ truy vấn của mình.

Bạn có thể thoát khỏi việc viết lại truy vấn của mình bằng cách sử dụng LEFT OUTER JOINvà kiểm tra không có một hàng nào trong phạm vi nhỏ bằng cách kiểm traNULL

If EXISTS(
    SELECT databasename
    FROM somedb.dbo.bigtable l
    LEFT JOIN dbo.smalltable c ON c.source = l.source
    WHERE databasename = 'someval'
    AND source <> 'kt'
    AND c.source IS NULL
)

Bạn cũng có thể sử dụng một EXCEPTtruy vấn, tùy thuộc vào số lượng trường bạn cần so sánh như thế này:

If EXISTS(
   SELECT source
   FROM somedb.dbo.bigtable l
   WHERE databasename = 'someval'
   AND source <> 'kt'

   EXCEPT

   SELECT source
   FROM dbo.smalltable
)

Nhắc bạn, Aaron Bertrand có một bài đăng trên blog cung cấp lý do tại sao anh ấy không thích EXISTS mà bạn nên đọc qua để xem các phương pháp khác có hiệu quả hơn không, và nhận thức được các vấn đề chính xác tiềm ẩn trong trường hợp giá trị NULL.

Câu hỏi và trả lời liên quan: NẾU EXISTS mất nhiều thời gian hơn câu lệnh chọn được nhúng


0

Bạn cần viết lại truy vấn của mình bằng các phép nối rõ ràng và chỉ định thao tác nối nào bạn muốn sử dụng (lặp, băm hoặc hợp nhất) như thế này.

If not exists(
    select databasename 
    from somedb.dbo.bigtable l
    inner hash join dbo.smalltable c 
        on c.source = l.source
where databasename ='someval' and source  <>'kt')
begin
    Raiserror('Source missing',16,1)
    Return
end

Khi sử dụng EXISTS hoặc KHÔNG EXISTS, SQL Server đã tạo kế hoạch truy vấn với hoạt động NESTED LOOP giả định rằng nó sẽ đi qua tất cả các hàng trong tập hợp tìm kiếm hàng đầu tiên để đáp ứng điều kiện. Sử dụng HASH THAM GIA sẽ tăng tốc nó.


Hơn bạn, sẽ kiểm tra nó
TheGameiswar

0

Tôi đã gặp phải cùng một vấn đề, tôi đã tự xoay sở bằng cách tránh sử dụng "EXISTS" và bằng cách sử dụng chức năng "COUNT ()" và câu lệnh "IF ... ELSE".

Ví dụ của bạn hãy thử như sau:

IF
(
    SELECT
        COUNT(l.databasename) + 1 AS databasename
    FROM somedb.dbo.bigtable AS l

    WHERE   l.databasename ='someval'
        AND l.[source]  <> 'kt'
        AND NOT EXISTS(SELECT 1 FROM dbo.smalltable AS c WHERE c.[source]=l.[source])
) > 1 --Acts like EXISTS
BEGIN
    RAISERROR('Source missing', 16, 1)
RETURN
END

Lý do tôi thêm "+ 1" vào số đếm là để tôi có thể sử dụng "> 1" trong điều kiện IF, sử dụng "> 0" hoặc "<> 0" sẽ kích hoạt truy vấn để sử dụng các vòng lặp lồng nhau thay vì HASH Trận đấu. Không nhìn vào lý do tại sao điều đó chính xác xảy ra sẽ rất thú vị để tìm hiểu lý do tại sao.

Mong rằng sẽ giúp!

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.