2008-10-06 44 views
406

Truy vấn nào nhanh hơn?NOT IN vs NOT EXISTS

NOT EXISTS:

SELECT ProductID, ProductName 
FROM Northwind..Products p 
WHERE NOT EXISTS (
    SELECT 1 
    FROM Northwind..[Order Details] od 
    WHERE p.ProductId = od.ProductId) 

Hoặc KHÔNG IN:

SELECT ProductID, ProductName 
FROM Northwind..Products p 
WHERE p.ProductID NOT IN (
    SELECT ProductID 
    FROM Northwind..[Order Details]) 

Kế hoạch thực hiện truy vấn nói rằng họ cả hai làm điều tương tự. Nếu đó là trường hợp, đó là hình thức được đề nghị?

Điều này được dựa trên cơ sở dữ liệu NorthWind.

[Chỉnh sửa]

Chỉ cần tìm thấy bài viết hữu ích này: http://weblogs.sqlteam.com/mladenp/archive/2007/05/18/60210.aspx

Tôi nghĩ rằng tôi sẽ gắn bó với không tồn tại.

+1

bạn đã thử kế hoạch bằng cách sử dụng kết nối bên trái có null không? – Sebas

+1

Tôi tự hỏi liệu Cơ sở dữ liệu có khác nhau không, nhưng trong điểm chuẩn mới nhất của tôi đối với PostgreSQL, truy vấn 'KHÔNG IN' này:" CHỌN "A". * FROM "A" WHERE "A". "Id" NOT IN (SELECT "B". " "TỪ" B "Ở ĐÂU" B "." Uid "= 2)' nhanh hơn gần 30 lần so với 'KHÔNG TUYỆT VỜI': 'CHỌN" A ". * TỪ" A "Ở ĐÂU (KHÔNG (DANH SÁCH (CHỌN 1 TỪ "B" WHERE "B". "User_id" = 2 AND "B". "Aid" = "A". "Id"))) ' –

+0

Có thể trùng lặp của [Sự khác nhau giữa NOT EXISTS vs. NOT IN vs LEFT JOIN WHERE IS NULL?] (Http://stackoverflow.com/questions/2246772/whats-the-difference-between-not-exists-vs-not-in-vs-left-join-where-is-null) – rcdmk

Trả lời

553

Tôi luôn đặt mặc định là NOT EXISTS.

Các kế hoạch thực hiện có thể giống nhau vào lúc này nhưng nếu một trong hai cột được thay đổi trong tương lai để cho phép NULL là phiên bản NOT IN sẽ cần phải làm việc nhiều hơn (ngay cả khi không NULL s đang thực sự hiện diện trong dữ liệu) và ngữ nghĩa của NOT IN nếu NULL s hiện tại dường như không phải là ngôn ngữ bạn muốn.

Khi không phải Products.ProductID hoặc [Order Details].ProductID cho phép NULL s NOT IN sẽ được xử lý giống hệt với truy vấn sau đây.

SELECT ProductID, 
     ProductName 
FROM Products p 
WHERE NOT EXISTS (SELECT * 
        FROM [Order Details] od 
        WHERE p.ProductId = od.ProductId) 

Gói chính xác có thể thay đổi nhưng đối với dữ liệu mẫu của tôi, tôi nhận được thông tin sau.

Neither NULL

Một quan niệm sai lầm một cách hợp lý thông thường dường như là phụ truy vấn tương quan luôn "xấu" so với tham gia. Họ chắc chắn có thể là khi họ buộc một kế hoạch vòng lặp lồng nhau (truy vấn phụ được đánh giá từng hàng) nhưng kế hoạch này bao gồm một toán tử hợp lý chống bán tham gia. Chống tham gia bán không bị giới hạn trong vòng lặp lồng nhau nhưng có thể sử dụng hàm băm hoặc hợp nhất (như trong ví dụ này) cũng tham gia.

/*Not valid syntax but better reflects the plan*/ 
SELECT p.ProductID, 
     p.ProductName 
FROM Products p 
     LEFT ANTI SEMI JOIN [Order Details] od 
     ON p.ProductId = od.ProductId 

Nếu [Order Details].ProductIDNULL -able truy vấn sau đó trở thành

SELECT ProductID, 
     ProductName 
FROM Products p 
WHERE NOT EXISTS (SELECT * 
        FROM [Order Details] od 
        WHERE p.ProductId = od.ProductId) 
     AND NOT EXISTS (SELECT * 
         FROM [Order Details] 
         WHERE ProductId IS NULL) 

Lý do cho điều này là ngữ nghĩa đúng nếu [Order Details] chứa bất kỳ NULLProductId s là để trở về không có kết quả. Xem thêm chống bán tham gia và hàng số spool để xác minh điều này được thêm vào kế hoạch.

One NULL

Nếu Products.ProductID cũng được thay đổi để trở thành NULL -able truy vấn sau đó trở thành

SELECT ProductID, 
     ProductName 
FROM Products p 
WHERE NOT EXISTS (SELECT * 
        FROM [Order Details] od 
        WHERE p.ProductId = od.ProductId) 
     AND NOT EXISTS (SELECT * 
         FROM [Order Details] 
         WHERE ProductId IS NULL) 
     AND NOT EXISTS (SELECT * 
         FROM (SELECT TOP 1 * 
           FROM [Order Details]) S 
         WHERE p.ProductID IS NULL) 

Lý do cho một trong đó là bởi vì một NULLProducts.ProductId không nên được trả lại trong kết quả trừ nếu truy vấn phụ NOT IN không trả lại kết quả nào (ví dụ: bảng [Order Details] trống). Trong trường hợp đó nó nên. Trong kế hoạch cho dữ liệu mẫu của tôi, điều này được thực hiện bằng cách thêm một liên kết chống bán khác như dưới đây.

Both NULL

Hiệu quả của việc này được thể hiện trong the blog post already linked by Buckley. Trong ví dụ có số lần đọc hợp lý tăng từ khoảng 400 đến 500.000.

Ngoài ra thực tế là một đơn NULL có thể làm giảm số lượng hàng bằng không làm cho ước tính số lượng thẻ rất khó. Nếu SQL Server giả định rằng điều này sẽ xảy ra nhưng trên thực tế không có các hàng NULL trong dữ liệu phần còn lại của kế hoạch thực hiện có thể tồi tệ hơn, nếu đây chỉ là một phần của truy vấn lớn hơn, with inappropriate nested loops causing repeated execution of an expensive sub tree for example.

Tuy nhiên, đây không phải là kế hoạch thực hiện duy nhất có thể cho số NOT IN trên cột NULL. This article shows another one cho truy vấn đối với cơ sở dữ liệu AdventureWorks2008.

Đối với NOT IN trên cột NOT NULL hoặc NOT EXISTS đối với cột có thể vô hiệu hoặc không có giá trị, nó cung cấp gói sau đây.

Not EXists

Khi những thay đổi cột để NULL -able kế hoạch NOT IN bây giờ trông như

Not In - Null

Nó bổ sung thêm một bên tham gia điều hành kế hoạch. Thiết bị này là explained here. Đó là tất cả để chuyển đổi các chỉ số tương quan duy nhất trước đó tìm kiếm trên Sales.SalesOrderDetail.ProductID = <correlated_product_id> đến hai tìm kiếm cho mỗi hàng bên ngoài. Phần bổ sung nằm trên WHERE Sales.SalesOrderDetail.ProductID IS NULL.

Vì đây là dưới sự tham gia chống bán nếu người đó trả về bất kỳ hàng nào, tìm kiếm thứ hai sẽ không xảy ra. Tuy nhiên nếu Sales.SalesOrderDetail không chứa bất kỳ NULLProductID s nó sẽ tăng gấp đôi số lượng hoạt động tìm kiếm bắt buộc.

+3

Tôi có thể hỏi làm thế nào bạn nhận được đồ thị lược tả như được hiển thị? – xis

+5

@xis Đây là những kế hoạch thực hiện được mở trong trình thám hiểm kế hoạch SQL Sentry. Bạn cũng có thể xem các kế hoạch thực hiện đồ họa trong SSMS. –

+0

Tôi đánh giá cao điều này vì lý do duy nhất là: "KHÔNG tồn tại" chức năng theo cách tôi mong đợi 'KHÔNG IN' để chức năng (trong đó, nó không). – levininja

22

Nếu người lập kế hoạch thực hiện cho biết chúng giống nhau, chúng giống nhau. Sử dụng cái nào sẽ làm cho ý định của bạn rõ ràng hơn - trong trường hợp này, cái thứ hai.

+2

thời gian lập kế hoạch thực hiện có thể giống nhau nhưng kết quả thực hiện có thể khác nhau do đó có một Sự khác biệt. NOT IN sẽ tạo ra kết quả không mong muốn nếu bạn có NULL trong tập dữ liệu của bạn (xem câu trả lời của buckley). Tốt nhất để sử dụng NOT EXISTS làm mặc định. – nanonerd

+0

Trong ví dụ này, ProductID là một khóa (không null) trường, vì vậy bất cứ điều gì ... – Philippe

0

Nó phụ thuộc ..

SELECT x.col 
FROM big_table x 
WHERE x.key IN(SELECT key FROM really_big_table); 

sẽ không có tương đối chậm là không nhiều để hạn chế kích thước của những gì mà kiểm tra truy vấn để xem nếu họ quan trọng là trong. EXISTS sẽ thích hợp hơn trong trường hợp này.

Nhưng, tùy thuộc vào trình tối ưu hóa của DBMS, điều này có thể không khác nhau.

Như một ví dụ khi EXISTS là tốt hơn

SELECT x.col 
FROM big_table x 
WHERE EXISTS(SELECT key FROM really_big_table WHERE key = x.key); 
    AND id = very_limiting_criteria 
+1

'IN' và' EXISTS' [có được cùng một kế hoạch trong SQL Server] (http://sqlinthewild.co.za/index. php/2009/08/17/tồn tại-vs-in /). Câu hỏi đặt ra là về 'NOT IN' và' NOT EXISTS'. –

+0

+1 cho "nó phụ thuộc ..". Câu trả lời sql cổ điển – whytheq

11

Thực ra, tôi tin rằng đây sẽ là nhanh nhất:

SELECT ProductID, ProductName 
    FROM Northwind..Products p 
      outer join Northwind..[Order Details] od on p.ProductId = od.ProductId) 
WHERE od.ProductId is null 
+2

Có thể không phải là nhanh nhất khi trình tối ưu hóa đang thực hiện công việc của nó, nhưng chắc chắn sẽ nhanh hơn khi không. –

+1

Ông có thể đã đơn giản hóa truy vấn của mình cho bài đăng này quá – Kip

+0

Đồng ý Gia nhập bên ngoài bên trái thường nhanh hơn truy vấn phụ. – HLGEM

5

Trong ví dụ cụ thể của bạn họ đều giống nhau, bởi vì tôi ưu hoa đã tìm những gì bạn đang cố gắng làm là giống nhau trong cả hai ví dụ. Nhưng có thể là trong các ví dụ không tầm thường, trình tối ưu hóa có thể không làm điều này, và trong trường hợp đó, có nhiều lý do để ưu tiên người khác.

KHÔNG IN nên được ưu tiên nếu bạn đang thử nghiệm nhiều hàng trong lựa chọn bên ngoài của mình. Truy vấn phụ bên trong câu lệnh NOT IN có thể được đánh giá ở đầu thực thi và bảng tạm thời có thể được kiểm tra với mỗi giá trị trong lựa chọn bên ngoài, thay vì chạy lại subselect mỗi lần như được yêu cầu với câu lệnh NOT EXISTS .

Nếu truy vấn phụ phải tương quan với lựa chọn bên ngoài, thì KHÔNG tồn tại, vì trình tối ưu hóa có thể phát hiện đơn giản hóa ngăn không cho bất kỳ bảng tạm thời nào thực hiện cùng chức năng.

0

Nếu trình tối ưu hóa cho biết chúng giống nhau thì hãy xem xét yếu tố con người. Tôi muốn thấy KHÔNG tồn tại :)

62

Cũng lưu ý rằng NOT IN không tương đương với NOT EXISTS khi nói đến null.

bài này giải thích nó rất tốt

http://sqlinthewild.co.za/index.php/2010/02/18/not-exists-vs-not-in/

Khi trở về subquery thậm chí một null, NOT IN sẽ không phù hợp với bất kỳ hàng.

Lý do cho việc này có thể được tìm thấy bằng cách xem chi tiết về hoạt động của NOT IN thực sự có nghĩa là gì.

Hãy nói rằng, đối với mục đích minh hoạ rằng có 4 hàng trong bảng gọi t, có một cột gọi là ID với giá trị 1..4

WHERE SomeValue NOT IN (SELECT AVal FROM t) 

tương đương với

WHERE SomeValue != (SELECT AVal FROM t WHERE ID=1) 
AND SomeValue != (SELECT AVal FROM t WHERE ID=2) 
AND SomeValue != (SELECT AVal FROM t WHERE ID=3) 
AND SomeValue != (SELECT AVal FROM t WHERE ID=4) 

Hãy nói thêm rằng AVal là NULL trong đó ID = 4. Do đó! = trả về so sánh KHÔNG CẦN. Bảng chân lý hợp lý cho các trạng thái AND là rằng UNKNOWN và TRUE là UNKNOWN, UNKNOWN và FALSE là FALSE. Có không có giá trị nào có thể được AND'd với UNKNOWN để tạo kết quả TRUE

Do đó, nếu bất kỳ hàng nào của truy vấn con đó trả về NULL, toàn bộ toán tử NOT IN sẽ đánh giá FALSE hoặc NULL và không có bản ghi nào được trả lại

2

Tôi đã sử dụng

SELECT * from TABLE1 WHERE Col1 NOT IN (SELECT Col1 FROM TABLE2) 

và thấy rằng nó đã đem lại kết quả sai (By sai ý tôi là không có kết quả). Như đã có một NULL trong TABLE2.Col1.

Trong khi thay đổi truy vấn để

SELECT * from TABLE1 T1 WHERE NOT EXISTS (SELECT Col1 FROM TABLE2 T2 WHERE T1.Col1 = T2.Col2) 

đã cho tôi kết quả chính xác.

Kể từ đó, tôi đã bắt đầu sử dụng NOT EXISTS ở mọi nơi.

6

Tôi có một bảng có khoảng 120.000 bản ghi và chỉ cần chọn những bảng không tồn tại (được kết hợp với cột VARCHAR) trong bốn bảng khác với số hàng khoảng 1500, 4000, 40000, 200. Tất cả bảng có chỉ mục duy nhất trên cột Varchar có liên quan.

NOT IN mất khoảng 10 phút, NOT EXISTS mất 4 giây.

Tôi có một truy vấn đệ quy mà có thể đã có một số phần untuned mà có thể đã góp phần vào 10 phút, nhưng lựa chọn nào khác tham gia 4 giây giải thích, ít nhất với tôi rằng NOT EXISTS là tốt hơn nhiều hoặc ít nhất đó INEXISTS không chính xác như nhau và luôn đáng để kiểm tra trước khi tiếp tục với mã.