Postgresql源码(116)提升子查询案例分析

0 总结

对于SQL:select * from student, (select * from score where sno > 2) s where student.sno = s.sno;

pullup在pull_up_subqueries函数内递归完成,分几步:

  1. 将内层rte score追加到上层rtbable中:rte1是student、rte2带subquery是子查询、rte3是score。
  2. 调整所有var的varno(从1指向3)、varlevelsup(本例不涉及);还有其他调整本例不涉及。
  3. 将上层代表子查询的rte2的subquery清空,但rte2不删除。
  4. 将上层jointree中,指向子查询的rte替换为 子查询中的FromExpr(sno > 2)。

在这里插入图片描述

1 待分析场景

drop table student;
create table student(sno int, sname varchar(10), ssex int);
insert into student values(1, 'stu1', 0);
insert into student values(2, 'stu2', 1);
insert into student values(3, 'stu3', 1);
insert into student values(4, 'stu4', 0);drop table course;
create table course(cno int, cname varchar(10), tno int);
insert into course values(10, 'meth', 1);
insert into course values(11, 'english', 2);drop table teacher;
create table teacher(tno int, tname varchar(10), tsex int);
insert into teacher values(1, 'te1', 1);
insert into teacher values(2, 'te2', 0);drop table score;
create table score (sno int, cno int, degree int);
insert into score values (1, 10, 100);
insert into score values (1, 11, 89);
insert into score values (2, 10, 99);
insert into score values (2, 11, 90);
insert into score values (3, 10, 87);
insert into score values (3, 11, 20);
insert into score values (4, 10, 60);
insert into score values (4, 11, 70);

带子查询的语句:select * from student, (select * from score where sno > 2) s where student.sno = s.sno;

set enable_hashjoin to off;
set enable_mergejoin to off;explain select * from student, (select * from score where sno > 2) s where student.sno = s.sno;QUERY PLAN
---------------------------------------------------------------------Nested Loop  (cost=0.00..11278.20 rows=3740 width=58)Join Filter: (student.sno = score.sno)->  Seq Scan on student  (cost=0.00..21.00 rows=1100 width=46)->  Materialize  (cost=0.00..38.90 rows=680 width=12)->  Seq Scan on score  (cost=0.00..35.50 rows=680 width=12)Filter: (sno > 2)||||||||||
||等价写法||
vvvvvvvvvvvexplain select * from student, score where score.sno > 2 and student.sno = score.sno;QUERY PLAN
---------------------------------------------------------------------Nested Loop  (cost=0.00..11278.20 rows=3740 width=58)Join Filter: (student.sno = score.sno)->  Seq Scan on student  (cost=0.00..21.00 rows=1100 width=46)->  Materialize  (cost=0.00..38.90 rows=680 width=12)->  Seq Scan on score  (cost=0.00..35.50 rows=680 width=12)Filter: (sno > 2)

pull_up_subqueries做的事情就是帮我们把子查询上拉了,下面分析上拉是如何做的。

select * from student, (select * from score where sno > 2) s where student.sno = s.sno;


Plannerinfo完整结构
在这里插入图片描述

注意pull_up_subqueries只对jointree做处理。

void
pull_up_subqueries(PlannerInfo *root)
{/* Top level of jointree must always be a FromExpr */Assert(IsA(root->parse->jointree, FromExpr));/* Recursion starts with no containing join nor appendrel */root->parse->jointree = (FromExpr *)pull_up_subqueries_recurse(root, (Node *) root->parse->jointree,NULL, NULL);/* We should still have a FromExpr */Assert(IsA(root->parse->jointree, FromExpr));
}

2 pull_up_subqueries流程分析

2.1 处理FromExpr下面挂的第一个RANGETBLREF(student表)

FromExpr的第一张表是student表,指向一个rtekind = RTE_RELATION普通表类型,无需做任何处理。
在这里插入图片描述

2.2 处理FromExpr下面挂的第二个RANGETBLREF(子查询)

FromExpr的第二个rte是子查询(select * from score where sno > 2) s,可以看到引用的rte结构的subquery指向了内层query:
在这里插入图片描述
开始进入pull_up_simple_subquery内部处理,进入路径:
在这里插入图片描述
在分析pull_up_simple_subquery前有两个准入条件:

  1. rte->rtekind == RTE_SUBQUERY
  2. is_simple_subquery:不全部列举了,其中重要的是子查询不能带有一些特殊的语法:
is_simple_subquery...if (subquery->hasAggs ||subquery->hasWindowFuncs ||subquery->hasTargetSRFs ||subquery->groupClause ||subquery->groupingSets ||subquery->havingQual ||subquery->sortClause ||subquery->distinctClause ||subquery->limitOffset ||subquery->limitCount ||subquery->hasForUpdate ||subquery->cteList)return false;...

2.3 进入pull_up_simple_subquery开始处理子查上拉

第一步:拿到rte指向的子查询的Query树,构造PlannerInfo开始处理。

static Node *
pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,JoinExpr *lowest_outer_join,AppendRelInfo *containing_appendrel)
{Query	   *subquery;Query	   *parse = root->parse;PlannerInfo *subroot;subquery = copyObject(rte->subquery);subroot = makeNode(PlannerInfo);subroot->parse = subquery;......

第二步:递归的处理sublink、subquery等。

	replace_empty_jointree(subquery);if (subquery->hasSubLinks)pull_up_sublinks(subroot);preprocess_function_rtes(subroot);pull_up_subqueries(subroot);

第三步:开始pull up

到这里subroot就是rte2的subquery子查询的结构还没有任何调整:
在这里插入图片描述

	/** Adjust level-0 varnos in subquery so that we can append its rangetable* to upper query's.  We have to fix the subquery's append_rel_list as* well.*/rtoffset = list_length(parse->rtable);  // 2OffsetVarNodes((Node *) subquery, rtoffset, 0);OffsetVarNodes((Node *) subroot->append_rel_list, rtoffset, 0);

parse是上层查询的,上层有两个rtable。因为要把子查询拉平,所以把子查询的varno的指向调整一下,因为是要append到父查询,所以直接加上父查询rte的个数就好了(这里是2)。加完了应该指向父查询rte的3的位置(现在父查询只有两个rte,3位置是空的)。

OffsetVarNodes((Node *) subquery, 2, 0);

  1. 调整var→varno:1→3。
  2. 调整rangetblref→rindex:1→3。
    在这里插入图片描述
	/** Upper-level vars in subquery are now one level closer to their parent* than before.*/IncrementVarSublevelsUp((Node *) subquery, -1, 1);IncrementVarSublevelsUp((Node *) subroot->append_rel_list, -1, 1);
  • 这一步调整的目的:因为varlevelsup=1表示引用上一层的列(相当于距离)这里拉平后,varlevelsup就需要-1了,因为距离少了1。
  • 在当前SQL中select * from student, (select * from score where sno > 2) s where student.sno = s.sno;,开始调整var→varlevelsup字段,注意这个字段表示当前查询中使用了上层的变量,但上面子查询中(select * from score where sno > 2)没有引用上层的任何列,所以子查询中的var→varlevelsup都是0。这一步调整不会有影响。
	/** Now append the adjusted rtable entries and their perminfos to upper* query. (We hold off until after fixing the upper rtable entries; no* point in running that code on the subquery ones too.)*/CombineRangeTables(&parse->rtable, &parse->rteperminfos,subquery->rtable, subquery->rteperminfos);

开始把子查询的RTE拷贝到上层,现在子查询里面的varno=3指向就对了。
在这里插入图片描述

	/** We no longer need the RTE's copy of the subquery's query tree.  Getting* rid of it saves nothing in particular so far as this level of query is* concerned; but if this query level is in turn pulled up into a parent,* we'd waste cycles copying the now-unused query tree.*/rte->subquery = NULL;

删除子查询RTE带的Query,注意现在还缺一个条件。
在这里插入图片描述

pull_up_simple_subqueryreturn (Node *) subquery->jointree;

返回一个jointree带着条件。
在这里插入图片描述
返回去后,在这里把fromlist指向的第二个rte(子查询)换成 上面计算好的jointree。

然后就拉平了。

pull_up_subqueries_recurse...else if (IsA(jtnode, FromExpr)){FromExpr   *f = (FromExpr *) jtnode;ListCell   *l;Assert(containing_appendrel == NULL);/* Recursively transform all the child nodes */foreach(l, f->fromlist){lfirst(l) = pull_up_subqueries_recurse(root, lfirst(l),lowest_outer_join,NULL);}}

最终效果对比

pullup前 vs pullup后
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/205838.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

磐舟CI使用说明及案例

整体介绍 磐舟作为一个devops产品,它具备基础的CI流水线功能。同时磐舟的流水线是完全基于云原生架构设计的,在使用时会有一些注意事项。这里首先我们要了解磐舟整体的流水线打包逻辑。 文档结构说明 一般来说,磐舟推荐单个业务的标准git库…

和田2023年群众舞蹈大赛总决赛圆满落幕!

11月19日,由中共和田地委宣传部主办,地区文旅局承办,地区文化馆、各县市文旅局协办,北京市援疆和田指挥部支持的和田地区2023年“大地欢歌 舞动和田”群众舞蹈大赛总决赛在和田市新夜市圆满落幕,比赛最终决出一等奖1名…

渗透工具---BurpSuite 插件开发之HelloWorld

本文主要记录如何利用burp官方的新版API即MontoyaApi 写helloworld(上一篇的demo使用旧版api写的,这篇及后续开发将采用新版api) 先看效果图 更多详细内容见下方 这里有更详细更全面的代码内容 以及配置相关的内容 https://mp.weixin.qq.co…

C/C++结构体给函数传参

C语言中结构体给函数传参有两种方法: 1.值传递(修改形参不改变原值) void fun(STUDENT student){……} int main(){fun(student); }2.引用传递(传的是地址,修改形参会改变原值) void fun(STUDENT * stud…

【高性能计算】CUDA,OpenCL,FPGA 加速,MPI

OpenCL OpenCL(Open Computing Language)是一种跨平台的GPU加速技术,由Khronos Group开发。OpenCL允许开发人员在不同的硬件平台上编写并行计算应用程序。 OpenCL使用C语言的子集来编写应用程序,并提供了一组API,可以…

实例讲解Simulink的MATLAB Function模块

内容 MATLAB Function是一个支持使用M语言编写模块功能,并能够将所编写的M语言生成C代码,用于开发桌面应用和嵌入式应用的模块。它支持的 MATLAB内建函数比 Fcn模块要广泛,除去基本的四则运算、逻辑操作符和关系操作符,还可以调用MATLAB各种…

jetpack compose中实现丝滑的轮播图效果

写在前面 最近在翻Jetpack库,发现了DataStore,官方是这么说的: Jetpack DataStore 是一种数据存储解决方案,允许您使用协议缓冲区存储键值对或类型化对象。DataStore 使用 Kotlin 协程和 Flow 以异步、一致的事务方式存储数据。 …

双亲委派机制与类加载器的自定义

类加载器的作用与位置 在java字节码文件交给JVM运行时,需要类加载器子系统进行加载,类加载器子系统主要分为 三个阶段加载 链接 初始化具体可以看我前两篇博客这里就不再赘述 加载阶段主要有三个加载器引导类加载器 扩展类加载器 系统类加载器完成 他…

轻松整理文件夹,将视频文件全部归类到另一个文件夹!

如果你需要整理文件夹中的文件,将同一类别的文件归纳到一起,可以更加方便地管理和查找。现在,我们有一个简单而实用的方法,可以将文件夹中的所有视频文件归类到另一个文件夹中,让你的文件管理更加有序和高效。 首先&am…

vivado调试核ILA使用技巧

ILA(Integrated Logic Analyzer)IP Core 是 vivado 的调试核,具体如何创建、调用就不详说了。这里主要记录一个 ILA 的使用技巧,可以大大提高调试效率。 在 Program Device 后,如果添加了 ILA 核,则会出现以…

分布式进阶-链路追踪SpringCloudSleuth、Zipkin【实战篇】

一、前言 我们在使用微服务的时候,往往设计到各个微服务之间的调用,肯定会存在深度的调用链路,如果出现BUG或者异常,就会让问题定位和处理效率非常低。 有了Sleuth ,就可以帮助我们记录、跟踪应用程序中的请求和操作。…

基于springboot实现乒乓球预约管理系统项目【项目源码】计算机毕业设计

基于springboot实现乒乓球预约管理系统演示 系统的开发环境 浏览器:IE 8.1(推荐6.0以上) 开发使用语言:JAVA JDK版本:JDK_8 数据库管理系统软件:Mysql 运行平台:Windows 7 运行环境&#…