2014-10-30 31 views
6

Tôi có bảng Khách hàng và bảng đơn đặt hàng. Không phải tất cả khách hàng đã đặt hàng, vì vậy không phải tất cả ID khách hàng đều có trên bảng đơn đặt hàng. Tôi muốn kết quả của tôi chỉ hiển thị các giá trị từ bảng khách hàng không đặt hàng, vì vậy cột orderID sẽ hiển thị là rỗng. Các mã sau đây hoạt động:Tại sao câu lệnh này chỉ hoạt động với WHERE?

SELECT c.CustomerID, c.CustomerName, o.OrderID 
    FROM Customers c 
    LEFT OUTER JOIN Orders o 
ON c.CustomerID = o.CustomerID WHERE o.OrderID IS NULL 

Nhưng người tôi đã cố gắng ban đầu không:

SELECT c.CustomerID, c.CustomerName, o.OrderID 
    FROM Customers c 
    LEFT OUTER JOIN Orders o 
ON c.CustomerID = o.CustomerID AND o.OrderID IS NULL 

Cái này thay vì cho thấy tất cả các giá trị trên bảng khách hàng nhưng tất cả trong số họ có null trên cho OrderID của họ

Tôi không nghĩ rằng tôi thực sự hiểu được sự khác biệt, vì tôi cảm thấy như vậy sẽ có ý nghĩa khi cả hai trở lại cùng một thứ.

+0

Tôi hiểu trực giác tại sao bạn nghĩ như vậy, nhưng tôi nghĩ lý do là 'BẬT' được sử dụng để nối các bảng vào một điều kiện nhất định. Những gì bạn đang cố gắng làm là tìm các hàng WHERE một điều kiện là đúng sự thật. Tôi không biết nếu điều này là bất cứ nơi nào gần sự thật, nhưng đó là cách tôi đã đến để suy nghĩ về nó và biết khi nào sử dụng những gì. – AdamMc331

+0

AND o.OrderID là thứ gây rối loạn. Nó sẽ hoạt động chính xác cho đến khi nó chạm vào điều kiện AND .... – rvphx

Trả lời

2

Đây là bản chất của một tham gia bên ngoài (trong trường hợp này là một tham gia bên trái). Một tham gia bên trái có bảng chính của bạn (Khách hàng), phù hợp với nó bằng các tiêu chí để tham gia bảng (Orders). Đối với mỗi hàng trong Khách hàng không có kết quả phù hợp, không giống như tham gia bên trong, nó không loại bỏ hàng. Thay vào đó, nó thêm tất cả các trường từ Đơn đặt hàng nhưng đặt null vào chúng.

Nhìn vào ví dụ này:

 
Table A   Table B 
┌──────┬──────┐ ┌──────┬──────┐ 
│field1│field2│ │field3│field4│ 
├──────┼──────┤ ├──────┼──────┤ 
│A  │1  │ │1  │One │ 
│B  │2  │ │3  │Three │ 
│C  │3  │ └──────┴──────┘ 
└──────┴──────┘ 

Các bảng bên tham gia (giữa field2 và field3) là:

 
┌──────┬──────┬──────┬──────┐ 
│field1│field2│field3│field4│ 
├──────┼──────┤──────┼──────┤ 
│A  │1  │1  │One │ 
│C  │3  │3  │Three │ 
└──────┴──────┴──────┴──────┘ 

Nhưng các bảng bên ngoài tham gia phải cung cấp cho bạn tất cả các kỷ lục, và nếu không có kết quả phù hợp, hãy đặt null.

 
┌──────┬──────┬──────┬──────┐ 
│field1│field2│field3│field4│ 
├──────┼──────┤──────┼──────┤ 
│A  │1  │1  │One │ 
│B  │2  │NULL │NULL │⬅︎ No match 
│C  │3  │3  │Three │ 
└──────┴──────┴──────┴──────┘ 

Bây giờ điều gì sẽ xảy ra nếu không có kết quả nào trong bảng 2? Ví dụ, nếu bạn thêm một điều kiện không thể trong mệnh đề ON? Sau đó, tất cả các bản ghi trong kết quả sẽ trông giống như "Không phù hợp"

 
┌──────┬──────┬──────┬──────┐ 
│field1│field2│field3│field4│ 
├──────┼──────┤──────┼──────┤ 
│A  │1  │NULL │NULL │⬅︎ No match (because of impossible condition) 
│B  │2  │NULL │NULL │⬅︎ No match (because of impossible condition) 
│C  │3  │NULL │NULL │⬅︎ No match (because of impossible condition) 
└──────┴──────┴──────┴──────┘ 

Vì vậy, nó không quan trọng nếu không có trận đấu vì không có kỷ lục trong Table2 với ID nhất định, hoặc nếu không có phù hợp bởi vì bạn đã thêm một điều kiện không thể. Kết quả của phép nối ngoài là các trường được cho là đến từ Table2 sẽ được thay thế bằng null. Bởi vì đó là cách một phép nối ngoài được xác định.


Bây giờ các bảng thế giới thực:

Bạn không thực sự có bất cứ hồ sơ nào ở bảng Orders có OrderID là null (trừ khi bạn thiết kế nó rất nặng). Vì vậy, nếu bạn đặt điều kiện đó trong mệnh đề ON, nó sẽ không tìm thấy bản ghi nào đáp ứng tiêu chí của bạn. Trong trường hợp này, bởi vì đây là một bên ngoài (bên trái) tham gia, bạn nhận được tất cả các bản ghi khách hàng origintal, và bởi vì không có trận đấu, mỗi người trong số họ có các lĩnh vực từ đơn đặt hàng tất cả null.

Trong trường hợp bạn đặt điều kiện trong WHERE, bạn đã thực sự sử dụng tốt hành vi này của việc kết nối trái. Bạn đã kết hợp từng khách hàng với thứ tự của nó. Nếu có một kết quả phù hợp - bạn đã nhận được ID đặt hàng thực tế. Nhưng trong trường hợp không có kết quả phù hợp - những thứ bạn đang tìm kiếm - nó sẽ thêm một ID thứ tự không hợp lệ.

Điều khoản where sau đó yêu cầu nó chỉ cung cấp cho bạn các bản ghi đã xảy ra. Đó là, các hồ sơ không có thứ tự khớp trong Đơn đặt hàng.

+0

Được rồi, giờ tôi đã hiểu tại sao AND không làm việc như thế nào. Nó sẽ chỉ nhìn vào bảng đơn đặt hàng, và nhìn vào cột orderID và sẽ thấy rằng không có gì là null. Nhưng tôi không hiểu tại sao nó đặt chúng là null @RealSkeptic – FrostyStraw

+0

@FrostyStraw Giá trị NULL trong OrderID có nghĩa là không có bản ghi nào phù hợp với tiêu chí 'ON c.CustomerID = o.CustomerID AND o.OrderID IS NULL'. –

+0

@FrostyStraw Tôi đã thêm một số giải thích về bản chất của sự tham gia bên ngoài vào câu trả lời của tôi, điều mà tôi hy vọng sẽ giúp bạn hiểu lý do. – RealSkeptic

1

Nó đơn giản không chỉ là cách SQL được thiết kế. Từ http://dev.mysql.com/doc/refman/5.0/en/join.html

Điều kiện_expr được sử dụng với BẬT là bất kỳ biểu thức có điều kiện nào của dạng có thể được sử dụng trong mệnh đề WHERE. Nói chung, bạn nên sử dụng mệnh đề BẬT cho các điều kiện chỉ định cách tham gia các bảng và mệnh đề WH2 để hạn chế những hàng nào bạn muốn trong tập hợp kết quả.

1

Thứ nhất là POST gia nhập Bảng. Thứ hai là một phần của sự tham gia.

Vì vậy, để đặt bằng tiếng Anh.

Đầu tiên:

Get CustomerID, Name, and OrderID from 
Customers Link to Orders where CustomerID matches 
Show where link failed 

Thứ hai:

Get CustomerID, Name, and OrderID from 
Customers Link to Orders where CustomerID matches and Order ID is null 

Tất cả các liên kết thất bại trong lần thứ hai, để lại gì để lọc kết quả của bạn.

3

Sử dụng LEFT OUTER JOIN bạn nhận tất cả các bản ghi từ Customers bảng. Vì vậy, AND o.OrderID IS NULL điều kiện của bạn chỉ lọc các bản ghi từ bảng Orders nhưng không phải là bản ghi từ Customers. Nhưng những gì bạn cần là để lọc bảng Customers và nó không hoạt động vì loại JOIN.

Trong khi đó, sử dụng điều kiện WHERE được áp dụng cho toàn bộ bản ghi bất kể loại JOIN. Hãy thử thay thế LEFT OUTER JOIN bằng INNER JOIN và bạn nhận được kết quả tương tự cho cả hai CHỌN.

Trong SELECT thứ hai của bạn, bạn nhận giá trị NULL cho o.OrderID vì bạn đã chỉ định rằng nó phải là NULL trong điều kiện của bạn AND o.OrderID IS NULL. Không có bản ghi nào tồn tại trong bảng Orders và do đó giá trị NULL có nghĩa là không có bản ghi nào khớp với tiêu chí ON c.CustomerID = o.CustomerID AND o.OrderID IS NULL.

1

Đối với truy vấn thứ hai, điều kiện tham gia của bạn là sai cho mỗi hàng. Nếu bảng Order có một CustomerId thì nó cũng sẽ có một OrderId. Sau mỗi hàng bị loại trừ phần "bên trái" của phép nối chỉ cần đưa trở lại tất cả các hàng bị loại trừ khỏi bảng bên trái (Khách hàng). Trong trường hợp này nó mang lại tất cả chúng.

Đối với truy vấn đầu tiên, nó chỉ sai trên các hàng bạn muốn, do đó, việc nối trái chỉ khôi phục các hàng đó. Và sau đó điều kiện nơi có thể đưa ra các hàng bạn không muốn.

1

Tạo bảng ## khách hàng (Id nvarchar (5)) Tạo bảng ## (int Order_No, Id nvarchar (5)) theo thứ tự

Chèn vào ## giá trị khách hàng ('A'), (' B '), (' C ')

Chèn vào ## giá trị theo thứ tự (1, 'A'), (2, 'B'), (3, 'B')

Chọn c. * từ ## customer c tham gia trái ## Đặt hàng o trên c.Id = o.id nơi o.Order_No là null

Chọn ci d, o.order_no từ ## khách hàng c trái tham gia ## theo thứ tự o trên c.Id = o.id và o.Order_No là null

Chọn c.id, o.order_no từ ## khách hàng c LEFT jOIN ## theo thứ tự o trên c.Id = o.id và o.Order_No không phải là null

thử nó cho mình, bạn sẽ thấy sự khác biệt, mệnh đề where được đưa ra một bộ lọc

1

LEFT OUTER JOIN duy trì các hàng chưa được so khớp từ bảng bên trái (đầu tiên), nối chúng với hàng NULL trong hình dạng của bảng bên phải (thứ hai). Hãy lấy một ví dụ cụ thể. Say, Khách hàng bảng có {c1,"n1"}, {c2,"n2"} và Orders có {c1, o1} trường hợp 1: ngoài trái tham gia sẽ cho kết quả:

{c1,"n1", o1}, {c2,"n2", null} 

Bây giờ, bạn biết sự khác biệt về nơi và "AND". Trường hợp tìm ra nơi bạn có orderid null, cho AND, không có trường hợp như vậy mà id khách hàng phù hợp và id đơn đặt hàng là null

6

Điều quan trọng là phải trả lời rằng hai truy vấn là rất khác nhau. Truy vấn đầu tiên là:

SELECT c.CustomerID, c.CustomerName, o.OrderID 
FROM Customers c LEFT OUTER JOIN 
    Orders o 
    ON c.CustomerID = o.CustomerID 
WHERE o.OrderID IS NULL; 

Trả về tất cả khách hàng không có đơn đặt hàng tương ứng (hoặc o.OrderId là không). Nó làm điều đó bởi vì left outer join giữ tất cả các hàng trong bảng đầu tiên. Nếu không có kết quả phù hợp, thì tất cả các cột từ bảng thứ hai là NULL và mệnh đề where sẽ chọn các cột này.

Các truy vấn thứ hai:

SELECT c.CustomerID, c.CustomerName, o.OrderID 
FROM Customers c LEFT OUTER JOIN 
    Orders o 
    ON c.CustomerID = o.CustomerID AND 
     o.OrderID IS NULL; 

tìm thấy tất cả các hàng tất cả khách hàng và cũng có thể nhận được thông tin đặt hàng nơi OrderId là null, nếu bất cứ hồ sơ như vậy tồn tại. Không có bộ lọc nào là Customers, vì left outer join đảm bảo rằng tất cả các hàng từ bảng đầu tiên đều nằm trong tập hợp kết quả.

Tôi sẽ ngạc nhiên nếu một trường được gọi là OrderId từng nhận giá trị NULL. Tuy nhiên, mỗi truy vấn là SQL hợp lệ và mỗi truy vấn đều hữu ích. Tuy nhiên, chỉ một người trong số họ làm những gì bạn dự định.

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