2009-08-31 29 views
11

Cách hiệu quả nhất để viết một câu lệnh chọn tương tự như dưới đây.Cách hiệu quả nhất để viết câu lệnh chọn với truy vấn phụ "không phải là" là gì?

SELECT * 
FROM Orders 
WHERE Orders.Order_ID not in (Select Order_ID FROM HeldOrders) 

Ý chính là bạn muốn bản ghi từ một bảng khi mặt hàng không nằm trong một bảng khác.

+5

Cách tốt nhất là thử các cách tiếp cận khác nhau và kiểm tra các kế hoạch thực thi. – pjp

+0

Trong tình huống của tôi SQL Server 2000, cho các chỉ mục trên các bảng được đề cập đến, truy vấn "Tham gia" là nhanh nhất. CHỌN * TỪ Đơn đặt hàng o TÌM HIỂU HeldOrders h trên o.Order_ID = h.Order_ID và h.Order_ID là null – Stimy

Trả lời

7

"Hầu hết các hiệu quả" sẽ là khác nhau tùy thuộc vào kích thước bảng , chỉ mục, v.v. Nói cách khác nó sẽ khác nhau tùy thuộc vào trường hợp cụ thể bạn đang sử dụng.

Có ba cách tôi thường sử dụng để thực hiện những gì bạn muốn, tùy thuộc vào tình huống.

1. Ví dụ của bạn hoạt động tốt nếu Orders.order_id được lập chỉ mục và HeldOrders khá nhỏ.

2. Phương pháp khác là "subquery tương quan", đó là sự thay đổi nhỏ về những gì bạn có ...

SELECT * 
FROM Orders o 
WHERE Orders.Order_ID not in (Select Order_ID 
           FROM HeldOrders h 
           where h.order_id = o.order_id) 

Lưu ý việc bổ sung các mệnh đề where. Điều này có xu hướng hoạt động tốt hơn khi HeldOrders có một số lượng lớn các hàng. Order_ID cần được lập chỉ mục trong cả hai bảng.

3. Một phương pháp tôi sử dụng đôi khi còn lại bên ngoài tham gia ...

SELECT * 
FROM Orders o 
left outer join HeldOrders h on h.order_id = o.order_id 
where h.order_id is null 

Khi sử dụng bên ngoài trái tham gia, h.order_id sẽ có giá trị trong nó phù hợp với o.order_id khi có một hàng phù hợp. Nếu không có hàng phù hợp, h.order_id sẽ là NULL. Bằng cách kiểm tra các giá trị NULL trong mệnh đề where bạn có thể lọc trên tất cả mọi thứ không có khớp.

Mỗi biến thể này có thể hoạt động hiệu quả hơn hoặc ít hiệu quả hơn trong các trường hợp khác nhau.

+0

'@ Dave': tại sao bạn sử dụng' NOT IN' thay vì 'NOT EXISTS' trong phương thức' 2'? – Quassnoi

+1

@Quassnoi: Thành thật mà nói, có lẽ là một thói quen xấu. Sau khi đọc câu trả lời của bạn ở trên, tôi dự định bắt đầu sử dụng NOT EXISTS. –

+0

Tùy chọn 3 hoạt động tốt nhất trong kịch bản của tôi (SQL Server 2000 được cung cấp cho các chỉ mục bảng của tôi). Tôi nghĩ câu trả lời tốt nhất là kiểm tra một số phương pháp. – Stimy

4

Bạn có thể sử dụng LEFT OUTER JOIN và kiểm tra NULL trên bảng bên phải.

SELECT O1.* 
FROM Orders O1 
LEFT OUTER JOIN HeldOrders O2 
ON O1.Order_ID = O2.Order_Id 
WHERE O2.Order_Id IS NULL 
+1

Đây là * xa * là một phương pháp hiệu quả nhất. – Quassnoi

+0

Điều này không nhất thiết phải là phương pháp hiệu quả nhất. –

+0

Đó là hiệu quả hơn đáng kể so với một truy vấn phụ, mặc dù - ít nhất nó chỉ thực hiện đối với bảng thứ hai một lần, thay vì một lần/hàng. – SqlRyan

19

Đối với người mới bắt đầu, một liên kết đến một bài báo cũ trong blog của tôi về cách NOT IN vị làm việc trong SQL Server (và trong các hệ thống khác nữa):


Bạn có thể viết lại như sau:

Tuy nhiên,

, hầu hết các cơ sở dữ liệu sẽ xử lý các truy vấn này giống nhau.

Cả hai truy vấn này sẽ sử dụng một số loại ANTI JOIN.

này rất hữu ích cho SQL Server nếu bạn muốn kiểm tra hai hoặc nhiều cột, vì SQL Server không hỗ trợ cú pháp sau:

SELECT * 
FROM Orders o 
WHERE (col1, col2) NOT IN 
     (
     SELECT col1, col2 
     FROM HeldOrders ho 
     ) 

Lưu ý, tuy nhiên, NOT IN có thể được khôn lanh do cách nó đối xử với NULL giá trị.

Nếu Held.Orders là nullable, không có hồ sơ được tìm thấy và trở về subquery nhưng một đơn NULL, toàn bộ truy vấn sẽ trả gì (cả INNOT IN sẽ đánh giá để NULL trong trường hợp này).

Hãy xem xét những dữ liệu này:

Orders: 

OrderID 
--- 
1 

HeldOrders: 

OrderID 
--- 
2 
NULL 

truy vấn này:

SELECT * 
FROM Orders o 
WHERE OrderID NOT IN 
     (
     SELECT OrderID 
     FROM HeldOrders ho 
     ) 

sẽ trở lại , mà có lẽ không phải là điều bạn mong muốn.

Tuy nhiên, chương trình này:

SELECT * 
FROM Orders o 
WHERE NOT EXISTS 
     (
     SELECT NULL 
     FROM HeldOrders ho 
     WHERE ho.OrderID = o.OrderID 
     ) 

sẽ trả lại hàng với OrderID = 1.

Lưu ý rằng LEFT JOIN giải pháp được đề xuất bởi những người khác không phải là giải pháp hiệu quả nhất.

truy vấn này:

SELECT * 
FROM Orders o 
LEFT JOIN 
     HeldOrders ho 
ON  ho.OrderID = o.OrderID 
WHERE ho.OrderID IS NULL 

sẽ sử dụng một điều kiện lọc sẽ cần phải đánh giá và lọc ra tất cả phù hợp với hàng có thể được numerius

Một ANTI JOIN phương pháp được sử dụng bởi cả hai INEXISTS sẽ chỉ cần đảm bảo rằng bản ghi không tồn tại sau khi cho mỗi hàng trong Orders, do đó, nó sẽ loại bỏ tất cả các bản sao có thể có trước tiên:

  • NESTED LOOPS ANTI JOINMERGE ANTI JOIN sẽ chỉ bỏ qua các từ khóa trùng lặp khi đánh giá HeldOrders.
  • A HASH ANTI JOIN sẽ loại bỏ các bản sao khi tạo bảng băm.
+1

Lần đầu tiên tôi nhìn thấy một truy vấn con tương quan mà thực sự cần thiết để trở thành một truy vấn con tương quan mà tôi có thể grok trong chưa đầy 5 phút. Ước gì tôi biết thủ thuật này * năm * trước. –

+0

'@Philip Kelley': thủ thuật nào chính xác? – Quassnoi

+0

Bạn có ý nghĩa gì trong phần này: "Điều này rất hữu ích cho SQL Server nếu bạn muốn kiểm tra hai hoặc nhiều cột, vì SQL Server không hỗ trợ cú pháp này:". Bạn đang nói điều này không áp dụng cho SQL Server? Bạn đang thiếu một "không"? – Stimy

1

Tôi không chắc chắn những gì là hiệu quả nhất, nhưng tùy chọn khác bao gồm:

1. Use EXISTS 

SELECT * 
FROM ORDERS O 
WHERE NOT EXISTS (SELECT 1 
        FROM HeldOrders HO 
        WHERE O.Order_ID = HO.OrderID) 

2. Use EXCEPT 

SELECT O.Order_ID 
FROM ORDERS O 
EXCEPT 
SELECT HO.Order_ID 
FROM HeldOrders 
0

Hãy thử

SELECT * 
FROM Orders 
LEFT JOIN HeldOrders 
ON HeldOrders.Order_ID = Orders.Order_ID 
WHERE HeldOrders.Order_ID IS NULL 
Các vấn đề liên quan