SQL trái tham gia vs nhiều bảng trên dòng TỪ?


256

Hầu hết các phương ngữ SQL chấp nhận cả hai truy vấn sau:

SELECT a.foo, b.foo
FROM a, b
WHERE a.x = b.x

SELECT a.foo, b.foo
FROM a
LEFT JOIN b ON a.x = b.x

Bây giờ rõ ràng khi bạn cần một tham gia bên ngoài, cú pháp thứ hai là bắt buộc. Nhưng khi thực hiện một phép nối bên trong tại sao tôi lại thích cú pháp thứ hai hơn cú pháp thứ nhất (hoặc ngược lại)?


1
Guffa: Làm thế nào bạn tìm thấy điều đó? Mặc dù câu hỏi của tôi là thực hành tốt nhất hơn là "làm thế nào để tôi"
jmucchiello

Vì đó là cách thực hành tốt nhất, hãy biến nó thành Wiki.
Binoj Antony

1
Tôi không nghĩ có ai bình luận về màn trình diễn của hai người này. Bất cứ ai có thể xác nhận hoặc trích dẫn bất cứ điều gì hợp lý liên quan đến bất kỳ sự khác biệt đáng kể?
ahnbizcad

@ahnbizcad Hai truy vấn đã cho không làm điều tương tự. Cái đầu tiên trả về giống như một NGƯỜI THAM GIA VÀO. Việc triển khai là phiên bản cụ thể của DBMS và thậm chí sau đó có một vài đảm bảo. Nhưng các phép biến đổi DBMS tương đương các trường hợp dấu phẩy so với INNER THAM GIA TRÊN / WHERE vs CROSS THAM GIA Ở ĐÂU là chuyện nhỏ. Tìm hiểu về tối ưu hóa / thực hiện truy vấn cơ sở dữ liệu quan hệ.
philipxy

có một đề nghị tài nguyên? hướng dẫn khổng lồ, dày đặc là lý do tại sao tôi cố gắng học hỏi từ đây.
ahnbizcad

Câu trả lời:


319

Cú pháp cũ, chỉ liệt kê các bảng và sử dụng WHEREmệnh đề để chỉ định tiêu chí tham gia, đang bị phản đối trong hầu hết các cơ sở dữ liệu hiện đại.

Nó không chỉ để hiển thị, cú pháp cũ có khả năng mơ hồ khi bạn sử dụng cả INNER và OUTER tham gia trong cùng một truy vấn.

Tôi sẽ cho bạn một ví dụ.

Giả sử bạn có 3 bảng trong hệ thống của mình:

Company
Department
Employee

Mỗi bảng chứa nhiều hàng, được liên kết với nhau. Bạn có nhiều công ty và mỗi công ty có thể có nhiều bộ phận và mỗi bộ phận có thể có nhiều nhân viên.

Ok, vậy bây giờ bạn muốn làm như sau:

Liệt kê tất cả các công ty, và bao gồm tất cả các bộ phận của họ, và tất cả nhân viên của họ. Lưu ý rằng một số công ty chưa có bộ phận nào, nhưng hãy đảm bảo bạn cũng bao gồm họ. Hãy chắc chắn rằng bạn chỉ truy xuất các phòng ban có nhân viên, nhưng luôn liệt kê tất cả các công ty.

Vì vậy, bạn làm điều này:

SELECT * -- for simplicity
FROM Company, Department, Employee
WHERE Company.ID *= Department.CompanyID
  AND Department.ID = Employee.DepartmentID

Lưu ý rằng cái cuối cùng có một sự tham gia bên trong, để đáp ứng các tiêu chí mà bạn chỉ muốn các phòng ban với mọi người.

Ok, vậy chuyện gì xảy ra bây giờ. Vấn đề là, nó phụ thuộc vào công cụ cơ sở dữ liệu, trình tối ưu hóa truy vấn, chỉ mục và thống kê bảng. Hãy để tôi giải thích.

Nếu trình tối ưu hóa truy vấn xác định rằng cách để thực hiện việc này trước tiên là lấy một công ty, sau đó tìm các phòng ban và sau đó tham gia nội bộ với nhân viên, bạn sẽ không nhận được bất kỳ công ty nào không có bộ phận.

Lý do cho điều này là WHEREmệnh đề xác định hàng nào kết thúc trong kết quả cuối cùng, không phải là các phần riêng lẻ của hàng.

Và trong trường hợp này, do tham gia bên trái, cột Department.ID sẽ là NULL, và do đó, khi nói đến INNER THAM GIA cho nhân viên, không có cách nào để thực hiện ràng buộc đó đối với hàng Nhân viên, và vì vậy nó sẽ không xuất hiện.

Mặt khác, nếu trình tối ưu hóa truy vấn quyết định giải quyết việc tham gia của nhân viên bộ phận trước, sau đó thực hiện tham gia trái với các công ty, bạn sẽ thấy họ.

Vì vậy, cú pháp cũ là mơ hồ. Không có cách nào để xác định những gì bạn muốn, mà không xử lý các gợi ý truy vấn và một số cơ sở dữ liệu không có cách nào cả.

Nhập cú pháp mới, với điều này bạn có thể chọn.

Ví dụ: nếu bạn muốn tất cả các công ty, như mô tả vấn đề đã nêu, đây là những gì bạn sẽ viết:

SELECT *
FROM Company
     LEFT JOIN (
         Department INNER JOIN Employee ON Department.ID = Employee.DepartmentID
     ) ON Company.ID = Department.CompanyID

Tại đây, bạn xác định rằng bạn muốn nhân viên bộ phận tham gia được thực hiện như một người tham gia, và sau đó rời khỏi tham gia kết quả của điều đó với các công ty.

Ngoài ra, giả sử bạn chỉ muốn các phòng ban có chứa chữ X trong tên của họ. Một lần nữa, với kiểu tham gia cũ, bạn cũng có nguy cơ mất công ty, nếu nó không có bất kỳ bộ phận nào có tên X, nhưng với cú pháp mới, bạn có thể làm điều này:

SELECT *
FROM Company
     LEFT JOIN (
         Department INNER JOIN Employee ON Department.ID = Employee.DepartmentID
     ) ON Company.ID = Department.CompanyID AND Department.Name LIKE '%X%'

Mệnh đề bổ sung này được sử dụng để nối, nhưng không phải là bộ lọc cho toàn bộ hàng. Vì vậy, hàng có thể xuất hiện cùng với thông tin công ty, nhưng có thể có NULL trong tất cả các cột của bộ phận và nhân viên cho hàng đó, bởi vì không có bộ phận nào có chữ X trong tên của công ty đó. Điều này là khó với cú pháp cũ.

Đây là lý do tại sao, trong số các nhà cung cấp khác, Microsoft đã từ chối cú pháp nối ngoài cũ, nhưng không phải là cú pháp nối bên trong cũ, kể từ SQL Server 2005 trở lên. Cách duy nhất để nói chuyện với cơ sở dữ liệu chạy trên Microsoft SQL Server 2005 hoặc 2008, sử dụng cú pháp nối ngoài kiểu cũ, là đặt cơ sở dữ liệu đó ở chế độ tương thích 8.0 (còn gọi là SQL Server 2000).

Ngoài ra, cách cũ, bằng cách ném một loạt các bảng vào trình tối ưu hóa truy vấn, với một loạt các mệnh đề WHERE, giống như nói "bạn đang ở đây, làm tốt nhất có thể". Với cú pháp mới, trình tối ưu hóa truy vấn có ít việc phải làm để tìm ra phần nào đi cùng nhau.

Vì vậy, có bạn có nó.

TRÁI PHIẾU VÀ THAM GIA là làn sóng của tương lai.


28
"đang bị phản đối trong hầu hết các cơ sở dữ liệu hiện đại." --- chỉ tò mò, cái nào?
zerkms

10
tha lỗi cho tôi, tôi không quen với toán tử * =, nó làm gì? cảm ơn!
ultrajohn

9
Star = và = Star là (bên phải) bên phải và bên trái tham gia, hoặc đó là bên trái và bên phải? Bị từ chối từ lâu, tôi đã không sử dụng chúng kể từ SQL Server 6.
Tony Hopkinson

3
Dấu phẩy không được phản đối. OUTER JOINCú pháp không bao giờ chuẩn *=/ =*/ *=*không được dùng nữa.
philipxy

1
Câu trả lời này thậm chí không trả lời câu hỏi, mà không phải về các kết nối bên ngoài. Khiếu nại mà họ đưa ra về dấu phẩy so với INNER THAM GIA, tối ưu hóa lại, là sai.
philipxy

17

Cú pháp THAM GIA giữ các điều kiện gần bảng mà chúng áp dụng. Điều này đặc biệt hữu ích khi bạn tham gia một số lượng lớn các bảng.

Nhân tiện, bạn cũng có thể thực hiện một phép nối ngoài với cú pháp đầu tiên:

WHERE a.x = b.x(+)

Hoặc là

WHERE a.x *= b.x

Hoặc là

WHERE a.x = b.x or a.x not in (select x from b)

2
Cú pháp * = không được dùng trong MS SQLServer và vì lý do chính đáng: Không chỉ làm cho nó khó đọc hơn, mà nó còn không làm những gì mọi người nghĩ nó làm và nó cũng không giống như một TRÁI THƯỞNG tương tự. Cú pháp (+) không quen thuộc với tôi; SQL thực hiện điều gì?
Euro Micelli

2
Cú pháp khác được sử dụng bởi Oracle, ít nhất.
Lasse V. Karlsen

4
Không bao giờ sử dụng cú pháp Máy chủ SQL * =, nó sẽ KHÔNG cho kết quả nhất quán vì đôi khi nó sẽ hiểu là tham gia chéo không phải là tham gia trái. Điều này đúng ngay cả khi SQL Server 2000. Nếu bạn có bất kỳ mã nào sử dụng mã này, bạn cần sửa.
HLGEM

12

Cách đầu tiên là tiêu chuẩn cũ hơn. Phương thức thứ hai được giới thiệu trong SQL-92, http://en.wikipedia.org/wiki/Query . Tiêu chuẩn hoàn chỉnh có thể được xem tại http://www.contrib.andrew.cmu.edu/~shadow/sql/sql1992.txt .

Phải mất nhiều năm trước khi các công ty cơ sở dữ liệu áp dụng tiêu chuẩn SQL-92.

Vì vậy, lý do tại sao phương thức thứ hai được ưa thích, đó là tiêu chuẩn SQL theo ủy ban tiêu chuẩn ANSI và ISO.


,vẫn là tiêu chuẩn. onchỉ cần được giới thiệu cho outer joinmột lần subselects cũng được giới thiệu.
philipxy

12

Về cơ bản, khi mệnh đề TỪ của bạn liệt kê các bảng như vậy:

SELECT * FROM
  tableA, tableB, tableC

kết quả là một sản phẩm chéo của tất cả các hàng trong bảng A, B, C. Sau đó, bạn áp dụng hạn chế WHERE tableA.id = tableB.a_idsẽ loại bỏ một số lượng lớn các hàng, sau đó ... AND tableB.id = tableC.b_idvà sau đó bạn sẽ chỉ nhận được những hàng mà bạn thực sự quan tâm trong.

Các DBMS biết cách tối ưu hóa SQL này sao cho sự khác biệt về hiệu năng khi viết này bằng cách sử dụng THAM GIA là không đáng kể (nếu có). Sử dụng ký hiệu THAM GIA làm cho câu lệnh SQL dễ đọc hơn (IMHO, không sử dụng phép nối biến câu lệnh thành một mớ hỗn độn). Sử dụng sản phẩm chéo, bạn cần cung cấp tiêu chí tham gia trong mệnh đề WHERE và đó là vấn đề với ký hiệu. Bạn đang tập trung vào mệnh đề WHERE của bạn với những thứ như

    tableA.id = tableB.a_id 
AND tableB.id = tableC.b_id 

vốn chỉ được sử dụng để hạn chế sản phẩm chéo. Mệnh đề WHERE chỉ nên chứa HẠN CHẾ cho tập kết quả. Nếu bạn trộn các tiêu chí nối bảng với các hạn chế kết quả, bạn (và những người khác) sẽ thấy truy vấn của bạn khó đọc hơn. Bạn chắc chắn nên sử dụng THAM GIA và giữ mệnh đề TỪ một mệnh đề TỪ và mệnh đề WHERE là mệnh đề WHERE.


10

Thứ hai được ưa thích vì nó ít có khả năng dẫn đến một sự tham gia chéo tình cờ bằng cách quên đặt mệnh đề inthe where. Một phép nối không có mệnh đề on sẽ thất bại trong việc kiểm tra cú pháp, một phép nối kiểu cũ không có mệnh đề where sẽ không thất bại, nó sẽ thực hiện phép nối chéo.

Ngoài ra, sau này khi bạn phải tham gia bên trái, sẽ rất hữu ích cho việc bảo trì rằng tất cả chúng đều nằm trong cùng một cấu trúc. Và cú pháp cũ đã hết hạn từ năm 1992, đã đến lúc ngừng sử dụng nó.

Thêm vào đó, tôi đã phát hiện ra rằng nhiều người chỉ sử dụng cú pháp đầu tiên không thực sự hiểu các phép nối và hiểu các phép nối là rất quan trọng để có được kết quả chính xác khi truy vấn.


6

Tôi nghĩ rằng có một số lý do chính đáng trên trang này để áp dụng phương pháp THAM GIA rõ ràng sử dụng phương pháp thứ hai. Mặc dù vậy, khi tiêu chí THAM GIA được loại bỏ khỏi mệnh đề WHERE, việc xem các tiêu chí lựa chọn còn lại trong mệnh đề WHERE sẽ trở nên dễ dàng hơn nhiều.

Trong các câu lệnh CHỌN thực sự phức tạp, người đọc sẽ dễ dàng hiểu được những gì đang diễn ra.


5

Các SELECT * FROM table1, table2, ...cú pháp là ok cho một vài bảng, nhưng nó trở nên theo cấp số nhân ( không nhất thiết là một tuyên bố về mặt toán học chính xác ) khó khăn hơn để đọc như số lượng các bảng sẽ tăng lên.

Cú pháp THAM GIA khó viết hơn (lúc đầu), nhưng nó làm cho nó rõ ràng tiêu chí nào ảnh hưởng đến bảng nào. Điều này làm cho nó khó khăn hơn nhiều để làm cho một sai lầm.

Ngoài ra, nếu tất cả các phép nối là INNER, thì cả hai phiên bản đều tương đương nhau. Tuy nhiên, thời điểm bạn có OUTER tham gia bất cứ nơi nào trong bản tuyên bố, mọi thứ trở nên phức tạp hơn nhiều và hầu như đảm bảo rằng những gì bạn viết sẽ không truy vấn những gì bạn nghĩ bạn đã viết.


2

Khi bạn cần tham gia bên ngoài, cú pháp thứ hai không phải lúc nào cũng được yêu cầu:

Oracle:

SELECT a.foo, b.foo
  FROM a, b
 WHERE a.x = b.x(+)

MSSQLServer (mặc dù nó không được hỗ trợ trong phiên bản 2000) / Sybase:

SELECT a.foo, b.foo
  FROM a, b
 WHERE a.x *= b.x

Nhưng trở lại câu hỏi của bạn. Tôi không biết câu trả lời, nhưng có lẽ liên quan đến thực tế là việc tham gia là tự nhiên hơn (ít nhất là về mặt cú pháp) so với việc thêm một biểu thức vào mệnh đề where khi bạn đang thực hiện chính xác điều đó: tham gia .


Máy chủ SQL không chấp nhận cú pháp nối trái và ngay cả trong SQL Server 2000, nó sẽ không nhất quán cho kết quả chính xác (đôi khi nó không tham gia chéo thay vì nối trái) và không bao giờ được sử dụng trong SQL Server.
HLGEM

@HLGEM: Cảm ơn thông tin. Tôi sẽ CẬP NHẬT bài viết của tôi để phản ánh những gì bạn đang nói.
Pablo Santa Cruz

0

Tôi nghe rất nhiều người phàn nàn rằng điều đầu tiên là quá khó hiểu và điều đó không rõ ràng. Tôi không thấy vấn đề gì với nó, nhưng sau khi thảo luận, tôi sử dụng cái thứ hai ngay cả trên INNER THAM GIA để rõ ràng.


1
Tôi đã có thói quen không sử dụng cú pháp THAM GIA và thực hiện theo cách đầu tiên. Tôi phải thừa nhận rằng tôi vẫn bị mắc kẹt trong thói quen thường xuyên chỉ vì tôi nghĩ rằng bộ não của tôi đã được điều hòa để tuân theo logic đó, đôi khi tôi rất khó nghĩ về cú pháp tham gia.
TheTXI

3
Tôi cũng được dạy theo cách đó. Tôi đã thay đổi phong cách mã hóa của mình, bởi vì mọi người sẽ nhìn vào nó và không dễ dàng nhận ra điều gì đang xảy ra. Vì không có sự khác biệt logic và tôi không thể tìm thấy lý do nào để chọn cái trước hơn cái sau, tôi cảm thấy rằng tôi nên thích nghi với việc làm cho mã rõ ràng hơn để giúp người khác hiểu những gì tôi viết.
kemiller2002

0

Đối với cơ sở dữ liệu, cuối cùng họ giống nhau. Tuy nhiên, đối với bạn, bạn sẽ phải sử dụng cú pháp thứ hai đó trong một số tình huống. Vì mục đích chỉnh sửa các truy vấn cuối cùng phải sử dụng nó (phát hiện ra bạn cần tham gia trái nơi bạn đã tham gia thẳng) và để thống nhất, tôi chỉ tạo mẫu cho phương thức thứ 2. Nó sẽ làm cho việc đọc các truy vấn dễ dàng hơn.


0

Vâng, các truy vấn thứ nhất và thứ hai có thể mang lại kết quả khác nhau vì TRÁI PHIẾU bao gồm tất cả các bản ghi từ bảng đầu tiên, ngay cả khi không có bản ghi tương ứng trong bảng bên phải.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.