2013-05-08 103 views
7

Tôi có ba bảng:Vấn đề với INNER JOIN và LEFT/RIGHT OUTER JOIN

  • Orders
    • OrderId, int PK
    • ID khách hàng, int FK đến khách hàng, NULL phép


  • Khách hàng
    • ID khách hàng, int PK
    • CompanyId, int FK cho Công ty, NULL không được phép


  • companie s
    • CompanyId, int PK
    • Tên, nvarchar (50)

Tôi muốn chọn tất cả các đơn đặt hàng, không có vấn đề nếu họ có một khách hàng hay không, và nếu họ có một khách hàng sau đó cũng là tên công ty của khách hàng.

Nếu tôi sử dụng truy vấn này ...

SELECT Orders.OrderId, Customers.CustomerId, Companies.Name 
FROM Orders 
     LEFT OUTER JOIN Customers 
      ON Orders.CustomerId = Customers.CustomerId 
     INNER JOIN Companies 
      OM Customers.CompanyId = Companies.CompanyId 

... nó chỉ trả về đơn đặt hàng mà có một khách hàng. Nếu tôi thay INNER JOIN bởi LEFT OUTER JOIN ...

SELECT Orders.OrderId, Customers.CustomerId, Companies.Name 
FROM Orders 
     LEFT OUTER JOIN Customers 
      ON Orders.CustomerId = Customers.CustomerId 
     LEFT OUTER JOIN Companies 
      OM Customers.CompanyId = Companies.CompanyId 

... nó hoạt động nhưng tôi không hiểu tại sao điều này là cần thiết vì các mối quan hệ giữa CustomersCompanies được yêu cầu: Một khách hàng phải có một công ty.

Một phương pháp khác mà làm việc cũng có vẻ là:

SELECT Orders.OrderId, Customers.CustomerId, Companies.Name 
FROM Companies 
     INNER JOIN Customers 
      ON Companies.CompanyId = Customers.CompanyId 
     RIGHT OUTER JOIN Orders 
      OM Customers.CustomerId Orders.CustomerId 

truy vấn này có số lượng bên trong và bên ngoài tham gia mà tôi mong đợi nhưng vấn đề là nó là khó đọc đối với tôi vì tôi có truy vấn của tôi là truy vấn của đơn đặt hàng trong trường hợp đơn đặt hàng là "gốc" của lựa chọn chứ không phải công ty. Ngoài ra việc sử dụng RIGHT OUTER JOIN là khá xa lạ đối với tôi.

Truy vấn cuối cùng là một phần nhỏ của truy vấn được tạo bởi nhà thiết kế cho Báo cáo dịch vụ báo cáo SQL Server. Tôi đang cố gắng để viết truy vấn bằng tay mà không có bề mặt thiết kế bởi vì nó là quá đông đúc và tôi đang gặp vấn đề để duy trì truy vấn sau nhiều thay đổi và nhiều thay đổi được mong đợi trong tương lai. Vì vậy, tôi muốn cung cấp cho truy vấn một cấu trúc có thể đọc được bằng cách nào đó.

Câu hỏi:

  1. Tại sao không truy vấn 1 công việc như tôi mong đợi?
  2. Truy vấn 2 là giải pháp đúng mặc dù (hoặc vì?) Nó sử dụng hai THAM GIA TRÁI PHIẾU KHÁC?
  3. Truy vấn có phải là giải pháp đúng không?
  4. Có cách nào tốt hơn để viết truy vấn không?
  5. Có một số quy tắc chung về ngón tay cái và cách thực hành cách viết truy vấn với nhiều kết nối bên ngoài và bên trong theo cách dễ đọc không?
+0

Tôi không nói bạn sai nhưng làm thế nào để đơn đặt hàng không có khách hàng. Đơn đặt hàng là sự kết hợp giữa sản phẩm và khách hàng chắc chắn là – DavidB

+0

@DavidB: Không phải là mô hình thực. Chỉ cần nghĩ đến các lệnh "vô danh" mà một công ty sản xuất có đơn đặt hàng nội bộ để sản xuất trên cổ phiếu mà không cần khách hàng tham khảo ... hoặc một cái gì đó tương tự. – Slauma

+1

Một giải pháp ở đây là có hồ sơ khách hàng "ẩn danh" mà bạn có thể đối sánh với những điều này, thay vì không có khách hàng nào cả. Bạn có thể sẽ thấy cách tiếp cận này cũng giúp với nhiều báo cáo khác. –

Trả lời

11

Ngữ nghĩa, tham gia được xử lý theo thứ tự chúng xuất hiện trong mệnh đề from. (Họ có thể không được thực sự thực hiện theo thứ tự này do tối ưu hóa SQL, nhưng trật tự là rất quan trọng để xác định tập hợp kết quả.)

Vì vậy, khi bạn làm:

from orders left outer join customers inner join companies 

(Tôi đi ra ngoài các on khoản mà là một phân tâm cho mục đích này)

các SQL được hiểu là:.

from (orders left outer join customers) inner join companies 

Bạn đang làm một inner join, do đó các giá trị phải xuất hiện ở cả hai bên. Trong trường hợp của bạn, điều này sẽ làm hỏng hiệu lực của left outer join.

Bạn muốn:

from orders left outer join (customers inner join companies) 

Dưới đây là một số giải pháp.

Giải pháp ưa thích của tôi là sử dụng left outer join cho tất cả các kết nối. Trong thực tế, để dễ đọc và bảo trì, hầu hết mọi truy vấn tôi viết sẽ chỉ là left outer join hoặc [inner] join kết nối các bảng. Phải phân tích cú pháp thông qua truy vấn để hiểu ngữ nghĩa của các phép nối có vẻ là một nỗ lực không cần thiết, nếu bạn có thể viết các truy vấn theo một dạng nhất quán.

Một giải pháp khác là sử dụng dấu ngoặc đơn:

from orders left outer join (customers inner join companies) 

giải pháp khác là một subquery:

from orders left outer join (select . . . from customers inner join companies) cc 
+0

Cảm ơn bạn, lời giải thích tuyệt vời! Truy vấn con có bất kỳ tác động hiệu suất nào so với giải pháp dấu ngoặc đơn hay kế hoạch truy vấn sẽ giống nhau không? – Slauma

+2

@Slauma. . . Trong SQL Server, tôi không nghĩ rằng có một tác động. Động cơ tối ưu hóa toàn bộ truy vấn. Trong các cơ sở dữ liệu khác, nó có thể tạo ra sự khác biệt (ví dụ, MySQL, truy vấn các truy vấn phụ để kế hoạch truy vấn sẽ khác trong cơ sở dữ liệu đó). –

2

Query 1 đã INNER JOIN về Công ty, có nghĩa là một thứ tự cần phải có vaild khách hàng (CompanyID) Nếu bạn muốn sử dụng INNER JOIN, nó có thể được như thế này

SELECT Orders.OrderId, a.CustomerId, a.Name 
FROM Orders 
LEFT JOIN (
    SELECT Customers.CustomerId, Companies.Name 
    FROM Customers 
    INNER JOIN Companies 
      OM Customers.CompanyId = Companies.CompanyId 
) a 
    ON Orders.CustomerId = a.CustomerId 
4
  1. Truy vấn 1: Vì bạn có INNER JOIN trên Khách hàng, LEFT JOIN có hiệu quả là INNER JOIN.
  2. Truy vấn 2 là chính xác vì bạn muốn xem tất cả Đơn hàng bất kể chất lượng/điều kiện dữ liệu.
  3. Tôi muốn tránh RIGHT JOIN s nói chung vì nó gây nhầm lẫn cho một số nhà phát triển và do đó ít dễ đọc hơn.Nói chung, bạn có thể viết truy vấn của mình theo cách như vậy để làm điều tương tự với việc sử dụng hiệu quả LEFT JOIN s.
  4. Truy vấn 2 là đề xuất của tôi về một cái gì đó đơn giản như thế này.
  5. Một quy tắc chung ... Khi bạn giới thiệu một số OUTER JOIN vào truy vấn của mình, số JOIN s theo sau cũng phải là OUTER JOIN s. Nếu không, bạn MAY loại trừ các hàng bạn không có ý định.
+1

Tôi đã bắt đầu viết một câu trả lời nhưng sau khi một trong những popped tôi đã dừng lại bởi vì đây là chính xác những gì tôi sẽ nói về tất cả các vấn đề. Có lẽ 1. có thể sử dụng nhiều giải thích hơn nhưng quy tắc chung phải là: Sử dụng INNER JOINs của bạn trước (để lọc hàng) và LEFT JOINs sau (để nhận dữ liệu bổ sung). –

2

1) Nó không làm việc bởi vì khi bạn INNER JOIN để Companies bạn thực hiện nó cần thiết để tồn tại trong toàn bộ các tham gia, nhưng kể từ khi Customer không tồn tại cho trật tự không có cách nào để kết hợp một kỷ lục Companies trở lại thứ tự và do đó nó không được trả lại.

2) Tôi cho rằng bạn có thể sử dụng truy vấn thứ hai nếu bạn nhận được hồ sơ Customer không có công ty liên quan, nhưng nếu mối quan hệ giữa các bảng đó là 1 đến 1 thì sẽ ổn.

3) Truy vấn thứ ba là tốt, nhưng xấu xí. Bạn tham gia vào bảng công ty và khách hàng và sau đó nói rằng bất kể những gì trong đó resultset tôi muốn tất cả mọi thứ từ Orders.

4) Tôi có thể sẽ tham gia khách hàng và công ty trong một truy vấn con và để lại tham gia trở lại các đơn đặt hàng.

Query:

SELECT Orders.OrderId, 
     Subquery.CustomerId, 
     Subquery.Name 
FROM Orders 
LEFT OUTER JOIN 
     (Select Customers.CustomerID, 
       Companies.Name 
     From Customers 
     INNER JOIN Companies 
       ON Customers.CompanyId = Companies.CompanyId) Subquery 
     On Orders.CustomerID = Subquery.CustomerID 

5) Đây là dễ dàng hơn nhiều đã trả lời với một tìm kiếm google. Tôi chắc rằng có thông tin toàn diện hơn mà tôi có thể viết trong vài phút.

3

Bạn có thể ghi các tham gia của bạn lồng nhau như vậy để kết nối bên trái được thực hiện trên kết quả kết hợp của khách hàng và công ty thay vì tham gia bên trong được thực hiện trên kết quả kết hợp của đơn đặt hàng và khách hàng. Tôi về cơ bản chỉ cần di chuyển bên trong của bạn tham gia trước khi mệnh đề ON cho bên ngoài tham gia bên ngoài. Một người khác đã đề xuất dấu ngoặc đơn để có được kết quả này, cả hai cú pháp sẽ dẫn đến cùng một sự thực thi nếu bộ nhớ phục vụ.

SELECT Orders.OrderId, Customers.CustomerId, Companies.Name 
FROM Orders 
LEFT OUTER JOIN Customers 
    INNER JOIN Companies 
     ON Customers.CompanyId = Companies.CompanyId 
    ON Orders.CustomerId = Customers.CustomerId 
+0

Thực ra các dấu ngoặc đơn dường như không bắt buộc, tôi chỉ thử nghiệm nó. Cảm ơn! – Slauma

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