Gor*_*ddy 11 join sql-server database-internals
在 Craig Freedman 的博客Nested Loops Join 中,他解释了为什么嵌套循环联接不能支持右外联接:
问题是我们多次扫描内表——外连接的每一行扫描一次。在这些多次扫描期间,我们可能会多次遇到相同的内部行。在什么时候我们可以得出结论,特定的内部行没有或不会加入?
有人可以用一种非常简单和有教育意义的方式解释一下吗?
这是否意味着循环从外表 ( R1
) 开始并扫描内表 ( R2
)?
我知道对于R1
不与 连接的值,R2
应将其替换为 aNULL
以便结果集变为 ( NULL, R2
)。对我来说,R2
当R1
不加入时似乎不可能返回一个值,因为它不知道R2
要返回哪个值。但这不是它的解释方式。或者是吗?
SQL Server会在事实上优化(经常替换)RIGHT JOIN
用LEFT JOIN
,但问题是解释为什么它在技术上是不可能的NESTED LOOPS JOIN
使用/支持RIGHT JOIN
逻辑。
ype*_*eᵀᴹ 13
我在链接的文章中不喜欢的是“嵌套循环连接算法不支持右连接的逻辑连接运算符”的说法。
虽然有限制,但此时的措辞有点令人困惑。我希望以下内容能更好地解释:
嵌套 lop 连接算法涉及两个表(无论是基表还是先前操作的结果集都无关紧要),它们被命名为外表和内表,并且算法以不同的方式处理它们(在外层遍历“外”表)循环和内部循环中的“内部”表)。
所以,假设我们有一个连接:
A (some_type) JOIN B
Run Code Online (Sandbox Code Playgroud)
该算法可以执行为:
outer-loop-A nested-loop inner-loop-B
Run Code Online (Sandbox Code Playgroud)
或者:
outer-loop-B nested-loop inner-loop-A
Run Code Online (Sandbox Code Playgroud)
现在,如果 ( some_type
) is INNER
or CROSS
join,那么就没有限制了,planner 可以选择这两种方式中的任意一种(具有不同的性能特征,取决于集合的大小、joined 列的值分布、索引等. 通常算法中会选择最小的表作为“外层”表)。
但是当some_type
是LEFT
join时,只能使用:
outer-loop-A nested-loop inner-loop-B
Run Code Online (Sandbox Code Playgroud)
并不是
outer-loop-B nested-loop inner-loop-A
Run Code Online (Sandbox Code Playgroud)
而且由于 aRIGHT
始终可以重写为LEFT
连接,因此它具有相同的限制,相反。对于A RIGHT JOIN B
(可以重写为 a B LEFT JOIN A
)它只能使用:
outer-loop-B nested-loop inner-loop-A
Run Code Online (Sandbox Code Playgroud)
而不是相反的*。
同样的限制适用于左半连接、左反半连接、右半连接和右反半连接。
FULL
另一方面,连接不能用嵌套循环连接算法直接处理。这篇文章很好地解释了(接近尾声)如何将完全连接(并且由优化器)重写为左连接和左反半连接的联合,然后可以将其规划为两个嵌套循环(和一个联盟)。
* 正如 Dudu Markovitz 在他的回答中所解释的那样,可以使用相反的方式,但前提是我们修改了嵌套循环连接算法以最终具有额外的结构和额外的步骤。
Dav*_*itz 12
这里的主要问题是外执行的加入,使用嵌套循环,在相反的逻辑方式的技术途径,其中,内表是通过访问外环和外表通过访问内部循环。
给定表 A 和 B,让我们实现A LEFT JOIN B
.
A
--
1
2
B
_
1
3
Run Code Online (Sandbox Code Playgroud)
首先,让我们以“自然”的方式来做。
我们遍历 A。
我们访问记录 1。
我们遍历 B。
我们在 B 中找到记录 1 并输出1-1。
我们继续遍历 A。
我们访问记录 2。
我们遍历 B。
我们在 B 中找不到任何匹配项。
我们输出2-null。
现在,让我们以“相反”的方式来做。
我们遍历 B。
我们访问记录 1。
我们遍历 A。
我们在 A 中找到记录 1 并输出1-1。
我们不断迭代 B。
我们访问记录 3。
我们迭代 A。
我们在 A 中找不到任何匹配项。
现在记住它是A LEFT JOIN B
,这意味着除了1-1我们还应该输出2-null。
问题是,在这一点上,我们不知道哪些记录 id A 已经匹配 (1),哪些记录没有匹配 (2)。
这实际上可以通过多种方式解决,例如通过为表 A 保存一个位数组。
当发现 A 记录作为匹配项时,我们将其标记在位数组中。
在嵌套循环结束时,我们将遍历位数组并输出和输出任何未标记的记录。
这显然比“自然”嵌套循环更复杂。