2012-02-02 38 views
21

Tôi chỉ đọc về TRỪ và INTERSECT trong thư viện MSDN và đã xem qua ví dụ này về cách sử dụng INTERSECT:Tại sao EXCEPT tồn tại trong T-SQL?

USE AdventureWorks2008R2 GO 
SELECT ProductID 
FROM Production.Product 
INTERSECT 
SELECT ProductID 
FROM Production.WorkOrder ; 
--Result: 238 Rows (products that have work orders) 

Có lẽ tôi là cổ hủ, nhưng tôi thường sẽ sử dụng đoạn mã sau để đạt được cùng một kết quả:

SELECT P.ProductID 
FROM Production.Product P 
INNER JOIN Production.WorkOrder W ON W.ProductID = P.ProductID 

Tôi có thiếu thứ gì đó hoặc INTERSECT giống với INNER JOIN không? Có một lợi ích hiệu suất để sử dụng một trong những khác?

Câu hỏi tương tự cho EXCEPT. Làm thế nào là thế này:

USE AdventureWorks2008R2; 
GO 
SELECT ProductID 
FROM Production.Product 
EXCEPT 
SELECT ProductID 
FROM Production.WorkOrder ; 
--Result: 266 Rows (products without work orders) 

khác nhau từ này:

SELECT P.ProductID 
FROM Production.Product P 
LEFT JOIN Production.WorkOrder W ON W.ProductID = P.ProductID 
WHERE W.ProductID IS NULL 

?

+2

-1: Đó không phải là cách INTERSECT hoạt động. Ví dụ đầu tiên của bạn không trả lại các sản phẩm có đơn đặt hàng công việc; nó trả về ID của bất kỳ sản phẩm nào có cùng id như id của một đơn đặt hàng công việc cho một sản phẩm. –

Trả lời

22

Tôi sẽ tập trung vào EXCEPT chỉ vì tôi quen thuộc hơn với nó. Ngoài ra, như một tuyên bố từ chối trách nhiệm, các ví dụ của tôi sẽ có trong Sqlite, vì tôi đang ở trên một hộp Linux. Tuy nhiên, cả Sqlite và SQL Server đều phải hỗ trợ chức năng này.

Cả hai INTERSECTEXCEPT là các toán tử được đặt, bắt nguồn từ các ý tưởng cơ bản trong relational algebra. Chúng hoạt động trên giá trị riêng biệt, được đặt toán tử.

Ví dụ của bạn là đơn giản. Tôi sẽ đưa ra một ví dụ, sử dụng một phiên bản Sqlite của cơ sở dữ liệu mẫu Northwind.

Giả sử bạn muốn nhận CustomerID của tất cả khách hàng đã đặt hàng với EmployeeID là 5, nhưng KHÔNG phải những người đã đặt hàng với EmployeeID là 6. Điều này đơn giản và tự nhiên với EXCEPT.

SELECT CustomerID FROM orders 
WHERE EmployeeID = 5 
EXCEPT 
SELECT CustomerID FROM orders 
WHERE EmployeeID = 6 

Điều này trả về 14 hàng trên phiên bản Northwind của tôi.

Giả sử bạn quyết định viết lại điều này bằng cách sử dụng JOIN s. Có thể một cái gì đó như thế này?

SELECT o1.CustomerID 
FROM orders o1 INNER JOIN orders o2 ON o1.CustomerID = o2.CustomerID 
WHERE o1.EmployeeID = 5 AND o2.EmployeeID != 6 

Rất tiếc, 525 hàng. Có thể thêm DISTINCT?

SELECT DISTINCT o1.CustomerID 
FROM orders o1 INNER JOIN orders o2 ON o1.CustomerID = o2.CustomerID 
WHERE o1.EmployeeID = 5 AND o2.EmployeeID != 6 

Bây giờ là 28 hàng, vẫn còn nhiều hơn những gì chúng tôi nhận được với EXCEPT.Lý do là điều này không loại bỏ CustomerID đã đặt hàng với 6. Thay vào đó, nó trả về tất cả CustomerID có đơn đặt hàng với 5 và một số EmployeeID khác hơn 6, cho dù họ có đơn đặt hàng hay không EmployeeID 6.

Tóm lại, EXCEPTINTERSECT là các toán tử được thiết lập so sánh hai truy vấn, trả về các bộ dữ liệu độc đáo và chắc chắn sử dụng chúng.

+3

Bạn có thể viết mã như o1.CustomerId chọn này từ ( chọn ID khách hàng từ đơn đặt hàng nơi EmployeeID = 5 ) như o1 ngoài trái tham gia ( chọn CustomerID từ đơn đặt hàng nơi EmployeeID = 6 ) như o2 trên o1. CustomerID = o2.CustomerID nơi o2.CustomerID là null – danmiao

0

Theo tôi EXCEPTINTERSECT được sử dụng để làm những điều tương tự như JOIN lệnh, nhưng nó đơn giản hơn với các bảng mà không có Primary Keys, ví dụ:

với INTERSECT:

SELECT FIRSTNAME, 
     LASTNAME, 
     ADDRESSLINE1, 
     CITY, 
     STATEPROVINCECODE, 
     POSTALCODE 
FROM MANAGER 
EXCEPT 
SELECT FIRSTNAME, 
     LASTNAME, 
     ADDRESSLINE1, 
     CITY, 
     STATEPROVINCECODE, 
     POSTALCODE 
FROM CUSTOMER 

Và để có kết quả tương tự với JOIN, bạn phải làm:

SELECT M.FIRSTNAME, 
     M.LASTNAME, 
     M.ADDRESSLINE1, 
     M.CITY, 
     M.STATEPROVINCECODE, 
     M.POSTALCODE 
FROM  MANAGER M 
WHERE NOT EXISTS (SELECT * 
        FROM CUSTOMER C 
        WHERE M.FIRSTNAME = C.FIRSTNAME 
          AND M.LASTNAME = C.LASTNAME 
          AND M.ADDRESSLINE1 = C.ADDRESSLINE1 
          AND M.CITY = C.CITY 
          AND M.POSTALCODE = C.POSTALCODE) 
GROUP BY M.FIRSTNAME,M.LASTNAME,M.ADDRESSLINE1,M.CITY, 
     M.STATEPROVINCECODE,M.POSTALCODE 

thông tin thêm here.

1

Ví dụ về truy vấn "tương đương" của bạn sai - truy vấn với INTERSECT không phải lúc nào cũng trả lại kết quả giống như INNER JOIN và giống nhau cho EXCEPTLEFT JOIN.

Nhìn vào ví dụ cụ thể về INTERSECT:

DECLARE @t TABLE(t INT NOT NULL) 
DECLARE @x TABLE(x INT NOT NULL) 

INSERT @t 
VALUES (1), (2), (3) 

INSERT @x VALUES(1), (1), (1) 

SELECT t FROM @t 
INTERSECT SELECT x FROM @x 

SELECT t FROM @t 
INNER JOIN @x ON x = t 

INTERSECT là giống như (nhưng không giống nhau) như IN khoản:

SELECT t FROM @t 
WHERE t IN (select x FROM @x) 

hoặc như EXISTS

SELECT t FROM @t 
WHERE EXISTS (select * FROM @x WHERE x = t) 

Các ví dụ tương tự bạn có thể thích ứng với điều khoản EXCEPT.

+0

Bạn đúng, nhưng hãy xem câu trả lời của tôi cho lý thuyết đằng sau nó ... – gbn

19
  • INTERSECT và EXCEPT là bán gia nhập
  • THAM GIA là equi-join

Vì vậy, khi bạn tham gia 2 bảng phù hợp, chẳng hạn, 5 hàng và 3 hàng

  • JOIN cho 15 hàng
  • INTERSECT cung cấp cho 3 hàng

TRỪ cũng tương tự như OUTER JOIN cho cùng một lý do

Trong khi chúng tôi đang ở trên về bán tham gia, sau đó chủ yếu

  • INTERSECT cho kết quả tương tự như EXISTS
  • TRỪ cung cấp cho các các kết quả tương tự như NOT EXISTS

"chủ yếu" đến vì cả INTERSECT và EXCEPT

Chỉnh sửa, bản demo nhanh của tất cả điều này

DECLARE @t1 TABLE (t1col INT); 
INSERT @t1 VALUES (1), (2), (2), (3), (3), (5), (5); 

DECLARE @t2 TABLE (t2col INT); 
INSERT @t2 VALUES (1), (2), (3), (4); 

SELECT 'INNER JOIN', * FROM @t1 t1 JOIN @t2 t2 ON t1.t1col = t2.t2col -- same both ways 

SELECT 't1 INTERSECT t2', * FROM @t1 INTERSECT SELECT 't1 INTERSECT t2', * FROM @t2; 

SELECT 't2 INTERSECT t1', * FROM @t2 INTERSECT SELECT 't2 INTERSECT t1', * FROM @t1; 

SELECT 't1 EXISTS t2', * FROM @t1 t1 
WHERE EXISTS (SELECT * FROM @t2 t2 WHERE t1.t1col = t2.t2col); 

SELECT 't2 EXISTS t1', * FROM @t2 t2 
WHERE EXISTS (SELECT * FROM @t1 t1 WHERE t1.t1col = t2.t2col); 

SELECT 't1 LEFT JOIN t2, IS NULL', * FROM @t1 t1 LEFT JOIN @t2 t2 ON t1.t1col = t2.t2col WHERE t2.t2col IS NULL 
SELECT 't2 LEFT JOIN t1, IS NULL', * FROM @t2 t2 LEFT JOIN @t1 t1 ON t1.t1col = t2.t2col WHERE t1.t1col IS NULL 

SELECT 't1 EXCEPT t2', * FROM @t1 EXCEPT SELECT 't1 EXCEPT t2', * FROM @t2; 

SELECT 't2 EXCEPT t1', * FROM @t2 EXCEPT SELECT 't2 EXCEPT t1', * FROM @t1; 

SELECT 't1 NOT EXISTS t2', * FROM @t1 t1 
WHERE NOT EXISTS (SELECT * FROM @t2 t2 WHERE t1.t1col = t2.t2col); 

SELECT 't2 NOT EXISTS t1', * FROM @t2 t2 
WHERE NOT EXISTS (SELECT * FROM @t1 t1 WHERE t1.t1col = t2.t2col); 

Cập nhật: Tháng 2 năm 2013.Đã thêm cột bổ sung để mô tả hoạt động

+0

+1 Toàn diện! –

+1

@Mikael Eriksson: Tôi sai, cố định. Tôi không có kịch bản demo đủ tốt trước – gbn

+0

Thực ra, nhìn lại, không phải là 'EXCEPT' một [antijoin] (http://en.wikipedia.org/wiki/Relational_algebra#Joins_and_join-like_operators), không phải là semijoin ? – voithos

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