Tại sao các vòng lặp lồng nhau chỉ hỗ trợ các phép nối trái?


11

Trong blog của Craig Freedman, Nested Loops Tham gia , anh giải thích lý do tại sao các vòng lặp lồng nhau không thể hỗ trợ tham gia bên ngoài bên phải:

Vấn đề là chúng tôi quét bảng bên trong nhiều lần - một lần cho mỗi hàng của phép nối ngoài. Chúng tôi có thể gặp cùng một hàng bên trong nhiều lần trong nhiều lần quét này. Tại thời điểm nào chúng ta có thể kết luận rằng một hàng bên trong cụ thể không hoặc sẽ không tham gia?

Ai đó có thể vui lòng giải thích điều này một cách thực sự đơn giản và giáo dục?

Có nghĩa là vòng lặp bắt đầu với bảng bên ngoài ( R1) và quét bên trong ( R2)?

Tôi hiểu rằng đối với một R1giá trị không tham gia cùng R2, nó nên được thay thế bằng một NULLtập hợp kết quả trở thành ( NULL, R2). Đối với tôi dường như không thể trả lại một R2giá trị khiR1 không tham gia, vì lý do là nó không thể biết R2giá trị nào sẽ trả về. Nhưng đó không phải là cách nó được giải thích. Hoặc là nó?

SQL Server thực tế tối ưu hóa (và thường thay thế) RIGHT JOINbằng LEFT JOIN, nhưng câu hỏi là để giải thích tại sao về mặt kỹ thuật không thể NESTED LOOPS JOINsử dụng / hỗ trợ RIGHT JOINlogic.

Câu trả lời:


12

Vấn đề chính ở đây là việc thực hiện nối ngoài, sử dụng các vòng lặp lồng nhau, theo cách kỹ thuật ngược với cách logic , trong đó bảng bên trong được truy cập thông qua vòng ngoàibảng ngoài được truy cập qua vòng trong .

Cho bảng A và B, hãy thực hiện A LEFT JOIN B.

A
--
1
2

B
_
1
3

Đầu tiên, hãy làm theo cách " tự nhiên ".

Chúng tôi lặp qua A.
Chúng tôi truy cập bản ghi 1.
Chúng tôi lặp qua B.
Chúng tôi tìm thấy bản ghi 1 trong B và đầu ra 1-1 .

Chúng tôi tiếp tục lặp qua A.
Chúng tôi truy cập vào bản ghi 2.
Chúng tôi lặp lại qua B.
Chúng tôi không tìm thấy bất kỳ trận đấu nào trong B.
Chúng tôi xuất 2-null .

Bây giờ, hãy làm theo cách " ngược lại ".

Chúng tôi lặp qua B.
Chúng tôi truy cập bản ghi 1.
Chúng tôi lặp qua A.
Chúng tôi tìm thấy bản ghi 1 trong A và đầu ra 1-1 .

Chúng tôi tiếp tục lặp qua B.
Chúng tôi truy cập hồ sơ 3.
Chúng tôi lặp lại qua A.
Chúng tôi không tìm thấy bất kỳ trận đấu nào trong A.

Bây giờ hãy nhớ rằng đó là A LEFT JOIN B, điều đó có nghĩa là ngoài 1-1 chúng ta nên xuất 2-null .
Vấn đề là tại thời điểm đó, chúng tôi không có ý tưởng nào về bản ghi id A mà chúng tôi đã có một trận đấu (1) và bản ghi nào chúng tôi không (2).


Điều này thực sự có thể được giải quyết bằng nhiều cách khác nhau, ví dụ bằng cách giữ một mảng bit cho bảng A.
Khi một bản ghi A được tìm thấy dưới dạng khớp, chúng ta đánh dấu nó trong mảng bit.
Ở cuối các vòng lặp lồng nhau, chúng ta sẽ đi qua mảng bit và đầu ra và xuất bất kỳ bản ghi nào không được đánh dấu.
Điều này rõ ràng phức tạp hơn vòng lặp lồng nhau "tự nhiên".


13

Điều tôi không thích trong bài viết được liên kết là tuyên bố rằng "thuật toán nối vòng lặp lồng nhau không hỗ trợ toán tử nối logic của phép nối đúng" .

Mặc dù có một hạn chế, từ ngữ tại thời điểm này hơi khó hiểu. Tôi hy vọng những điều sau đây giải thích mọi thứ tốt hơn:

Thuật toán nối lop lồng nhau bao gồm hai bảng (cho dù bảng cơ sở hoặc tập kết quả của các hoạt động trước đó không liên quan) được đặt tên bên ngoàibên trong bảng và chúng được xử lý theo một cách khác bởi thuật toán (bảng "bên ngoài" được dịch chuyển ở bên ngoài vòng lặp và bảng "bên trong" tại các vòng lặp bên trong).

Vì vậy, giả sử chúng ta có một tham gia:

A (some_type) JOIN B

Thuật toán có thể được thực thi như sau:

outer-loop-A  nested-loop  inner-loop-B

hoặc là:

outer-loop-B  nested-loop  inner-loop-A

Bây giờ, nếu ( some_type) là INNERhoặc CROSStham gia, thì không có giới hạn, trình hoạch định có thể chọn giữa một trong hai cách (với các đặc tính hiệu suất khác nhau, tùy thuộc vào kích thước của tập hợp, phân phối giá trị của các cột, chỉ mục, v.v. . Thông thường, bảng nhỏ nhất sẽ được chọn là bảng "bên ngoài" trong thuật toán).

Nhưng khi some_typeđược LEFTtham gia, nó chỉ có thể sử dụng:

outer-loop-A  nested-loop  inner-loop-B

và không

outer-loop-B  nested-loop  inner-loop-A

Và vì a RIGHTluôn có thể được viết lại dưới dạng LEFTtham gia, nên nó có cùng giới hạn, ngược lại. Đối với A RIGHT JOIN B(có thể viết lại a B LEFT JOIN A) nó chỉ có thể sử dụng:

outer-loop-B  nested-loop  inner-loop-A

và không phải là cách khác xung quanh * .

Giới hạn tương tự áp dụng cho trái-semijoin, trái-chống-semijoin, phải-semijoin và phải-chống-semijoin.

Mặt FULLkhác, phép nối không thể được xử lý trực tiếp bằng thuật toán nối vòng lặp lồng nhau. Bài viết giải thích rất rõ (gần cuối) cách viết lại một liên kết đầy đủ (và bằng trình tối ưu hóa) cho một liên kết của một liên kết trái và một chống semijoin trái mà sau đó có thể được lên kế hoạch như hai vòng lặp lồng nhau (và một liên hiệp).

* Như Dudu Markovitz giải thích trong câu trả lời của mình, cách ngược lại có thể được sử dụng nhưng chỉ khi chúng tôi sửa đổi thuật toán nối vòng lặp lồng nhau để có cấu trúc bổ sung và thêm một bước cuối cùng.


Vâng, điều đó làm rõ rất nhiều. Câu trả lời của bạn kết hợp với Dudu M: s giải thích rất rõ!
GordonLiddy
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.