暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

【手写数据库核心揭秘系列】第97讲 多表联接执行算子处理框架

开源无限 2025-06-06
31

💻 深耕数据库内核架构设计与开发十余年,曾主导多款高性能分布式数据库内核研发,攻克高并发、低延迟等核心技术难题。现倾力打造《从零手写数据库》系列教程,首次系统性公开数据库内核源码级实现细节!
🚀 从存储引擎、查询优化到分布式事务,手把手拆解核心模块;从语法解析树构建到执行计划生成,逐行代码还原设计精髓
🌟 无论你是数据库开发者、系统架构师,还是对底层技术充满好奇的极客,这里都有你想要的“硬核干货”!点击关注,与行业老兵共同探索数据库技术的星辰大海!

一、概述 


联接执行通过嵌套循环算法来实现,可以支持内联接,外联接两种操作,自然联接可以用前两种来代替。

二、嵌套循环框架 


联接执行算子的函数定义如下,它由嵌套循环方式来实现,整体包含两层无限循环,每次联接都会包含外表和内表两个数据源表。

    ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(line
    Node* ExecNestLoop(Node *planNode)
    {
        /* outer */
        for( ; ; )
        {
            /* inner */
            for( ; ; )
            {
            }
        }
        return NULL;
    }

    两层循环都是无限循环,何时才能跳出循环呢?

    • 对于外表,当作为外表的数据源表到达了结尾时,外层循环就会结束,也就意味着当前联接执行完成。
    • 对于内表来说,它的退出条件有两个,
      • 一是作为内表的数据源表达到了结尾;
      •  二是内表的查询结果符合联接条件,此时就会将外表和内表查询结果返回给上层。

    三、外表处理


    当needOuter标志为1时,会执行左子节点的执行函数,获取查询结果。

      ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(line
      if(nlNode->needOuter)
              {
                  lresult = ExecNodeProc(nlNode->leftplan);
                  if(lresult == NULL)
                  {
                      break;
                  }


                  nlNode->lresult = lresult;
                  nlNode->needOuter = 0;
                  nlNode->outerOnce = 0;


                  ExecReSeqScan(nlNode->rightplan);
              }
              else 
                  lresult = nlNode->lresult;

      外表的处理主要有:

      1. 当需要新的外表查询结果时,从左子节点获取查询结果;
      2. 将查询结果记录到当前执行节点当中;
      3. 每当外表获取新结果后,重置内表的扫描信息;
      4. 当不需要外表的查询结果时,将记录到执行节点中的外表查询记录取出。

      四、内表处理 


      当外表获得新的查询结果之后,就开始内表的处理。

      内表的处理主要有两大部分:

      • 一是获取内表的查询结果;
      • 二是执行联接表达式过滤。
        ounter(lineounter(lineounter(lineounter(lineounter(lineounter(line
                    rresult = ExecNodeProc(nlNode->rightplan);
                    if(rresult == NULL)
                    { 
                        break;            
                    }
                    nlNode->rresult = rresult;

        当从右子节点获取到查询结果后,将它记录到联接节点当中;

        如果右子节点查询结果为空时,说明内表已经到了结尾,退出内表的循环,并将需要外表查询标志置为1。

          ounter(line
          nlNode->needOuter = 1;

          在进行联接表达计算之前,需要将左右子节点的查询结果存入到结果列表中当,并传递给表达式计算函数。

            ounter(lineounter(lineounter(lineounter(lineounter(lineounter(line
                        resultList = (List*)MergeResult(lresult, rresult);
                        isJunk = ExecJoinExpr(nlNode, (Node*)resultList);
                        if(isJunk)
                        {
                            return (Node*)resultList;
                        }

            根据表达式计算结果,当结果为真时,返回当前结果集作为联接查询结果。

            五、总结 


            本节内容实现位于exam_52目录下的excutor.c文件中。

            🌟 点赞收藏,分享给身边的技术伙伴,关注我们,持续获取数据库内核开发的硬核干货!一起从源码级实现到分布式架构,解锁数据库技术的每一个核心细节!🚀


            【往期精彩推荐】

            【手写数据库核心揭秘系列】第96讲 联接表达式的物理执行计划
            【手写数据库内核miniToadb】第33天 表数据文件存储格式
            【手写数据库内核miniToadb】第10天 语法解析树的生成



            文章转载自开源无限,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

            评论