跟我一起读postgresql源码(十三)——Executor(查询执行模块之——Join节点(上)) (2)

循环嵌套连接的基本思想如下(以表R(左关系)与表S(右关系)连接为例):

FOR each tuple s in S DO FOR each tuple r in R DO IF r and s join to make a tuple t THEN output t;

为了迭代实现此方法,NestLoopState中定义了字段nl_NeedNewOuter和nl_MatchedOuter。当元组处于内层循环时,nl_NeedNewOuter为false,内层循环结束时nl_NeedNewOuter设置为true。为了能够处理Left Outer Join和Anti Join,需要知道内层循环是否找到了满足连接条件的内层元组,此信息由nl_MatchedOuter记录,当内层循环找到符合条件的元组时将其标记为true。

NestLoop执行过程主要是由ExecNestLoop函数来做。该函数主要是一个如上面提到的一个大循环。

该循环执行如下操作:

<1>如果nl_NeedNewOuter为true,则从左子节点获取元组,若获取的元组为NULL则返回空元组并结束执行过程。如果nLNeedNewOuter为false,则继续进行步骤2。

<2>从右子节点获取元组,若为NULL表明内层扫描完成,设置nl_NeedNewOuter为true,跳过步骤3继续循环。

<3>判断右子节点元组是否与当前左子节点元组符合连接条件,若符合则返回连接结果。

以上过程能够完成Inner Join的递归执行过程。但是为了支持其他几种连接则还需要如下两个特殊的处理:

1)当找到符合连接条件的元组后将nl_MatchedOuter标记为true。内层扫描完毕时,通过判断nl_MatchedOuter即可知道是否已经找到满足连接条件的元组,在处理Left Outer Join和Anti Join时需要进行与空元组(nl_NullInnerTupleSlot)的连接,然后将nLMatchedOuter设置为false。

2)当找到满足匹配条件的元组后,对于Semi JOIN和Anti JOIN方法需要设置nl_NeedNewOuter为true。区别在于Anti Join需要不满足连接条件才能返回,所以要跳过返回连接结果继续执行循环。

NestLoop节点的清理过程(ExecEndNestLoop函数)没有特殊处理,只需递归调用左右子节点的清理过程。

2.MergeJoin 节点

Mergejoin实现了对排序关系的归并连接算法,归并连接的输人都是已经排好序的。PostgreSQL中Mergejoin算法实现的伪代码如下:

Join { get initial outer and inner tuples INITIALIZE do forever { while (outer != inner) { SKIP_TEST if (outer < inner) advance outer SKIPOUTER_ADVANCE else advance inner SKIPINNER_ADVANCE } mark inner position SKIP_TEST do forever { while (outer == inner) { join tuples JOINTUPLES advance inner position NEXTINNER } advance outer position NEXTOUTER if (outer == mark) TESTOUTER restore inner position to mark TESTOUTER else break // return to top of outer loop } } }

算法首先初始化左右子节点,然后执行以下操作(其中对于大小的比较都是指对连接属性值的比较):

1)扫描到第一个匹配的位置,如果左子节点(outer)较大,从右子节点(inner)中获取元组;如果右子节点较大,从左子节点中获取元组。

2)标记右子节点当前的位置。

3)循环执行左子节点==右子节点判断,若符合则连接元组,并获取下一条右子节点元组,否则退出循环执行步骤4。

4)获取下一条左子节点元组。

5)如果左子节点==标记处的右子节点(说明该条左子节点与上一条相等),需要将右子节点扫描位置回退到扫描位置,并返冋步骤3;否则跳转到步骤1。

为了说明归并排序的连接算法,我们以Inner Join为例给出部分执行过程,两个current分别指向输人的当前元组,mark用于标记扫描的位置。

1)首先找到左右序列第一个匹配位置,下图中current(outer)=0小于Current(inner),因此outer的current向后移动。

跟我一起读postgresql源码(十三)——Executor(查询执行模块之——Join节点(上))

2)如图所示,当找到匹配项后,则进行连接,使用mark标记当前inner的扫描位置,并将inner的current向后移动。

跟我一起读postgresql源码(十三)——Executor(查询执行模块之——Join节点(上))

3)接着判断current(outer) = 1小于current(inner) =2,则将outer的current向后移动,并判断outer是否与mark相同(这是为了发现outer的current与前一个相同的情况)。

跟我一起读postgresql源码(十三)——Executor(查询执行模块之——Join节点(上))

4)下图显示current(outer) =2不等于mark(inner) = 1,则继续扫描过程。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpxxdf.html