当前位置: 首页 > news >正文

查询优化-提升子查询-UNION类型

文章目录

  • 文档用途
  • 详细信息

文档用途

剖析UNION类型子查询提升的条件和过程

详细信息

注:图片较大,可在浏览器新标签页打开。

SQL:

SELECT * FROM score sc, LATERAL(SELECT * FROM student WHERE sno = 1 UNION ALL SELECT * FROM student WHERE sno = sc .sno) st WHERE st.sno > 0;
查询树结构:

分析该查询树,主查询包含2个RangeTblEntry:sc和st;其中st这个表的类型是子查询,包含2个RangeTblEntry,从SQL也可以看出这2个RangeTblEntry对应两个select查询,按照Query结构去分层该查询树为3层。
https://github.com/nullemp/postgres_notes/blob/master/image/subquery.node.jpg?raw=true
稍微简化一下,结构如下图所示:

打印该SQL的执行计划:

提升子查询后查询树结构:
https://github.com/nullemp/postgres_notes/blob/master/image/subquery_opti.node.jpg?raw=true
根据执行计划和查询树优化前后对比,对于UNION类型的子查询提升主要是将UNION两侧子查询提升,反映在查询树中即是这2个子查询类型的RangeTblEntry添加到主查询对应的rtable队列中,3层查询优化为2层查询结构。

提升流程:查找范围表中可以提升到父查询中的子查询。如果子查询没有特殊的特性,比如分组/聚合,那么我们可以将其合并到父查询的联接树中。此外,简单的 UNION ALL 结构的子查询可以转换为“追加关系”。

void pull_up_subqueries(PlannerInfo *root)

{

Assert(IsA(root->parse->jointree, FromExpr));

root->parse->jointree = (FromExpr *)

pull_up_subqueries_recurse(root, (Node *) root->parse->jointree,

NULL, NULL);

Assert(IsA(root->parse->jointree, FromExpr));

}

jointree中包含了FROM…WHERE…所引用的表,该递归结构通过pull_up_subqueries_recurse对其进行递归处理,所以优化执行时先去深度遍历FromExpr中的列表中的每一项成员:

if (IsA(jtnode, FromExpr))

{

FromExpr *f = (FromExpr *) jtnode;

ListCell *l;

Assert(containing_appendrel == NULL);

foreach(l, f->fromlist)

{

lfirst(l) = pull_up_subqueries_recurse(root, lfirst(l),

lowest_outer_join,

NULL);

}

}

如果RangeTblEntry是subquery类型并且满足简单子查询条件,使用pull_up_simple_union_all处理,该函数接受3个参数,分别是:查询树上下文, RangeTblRef, RangeTblEntry。

int varno = ((RangeTblRef *) jtnode)->rtindex;

RangeTblEntry *rte = rt_fetch(varno, root->parse->rtable);

if (rte->rtekind == RTE_SUBQUERY &&

is_simple_union_all(rte->subquery))

return pull_up_simple_union_all(root, jtnode, rte);

pull_up_simple_union_all:

根据优化后的查询树结构,提升的主要目的是把三个层次变成两个层次,那么如果“子子查询”中引用了顶层的列属性,那么这些变量应该提升一个层次,也就是调用incrementVarSublevelsUp_ rtable(rtable, -1 , 1 )。比如本例SQL:SELECT * FROM student WHERE sno = sc .sno , sc.sno 就引用了第一个层次中的列表量,它的 Var >varlevlesup 的原值是 2(相对值),子查询提升之后应该变成1。

2.下发LATERAL,本例中是(SELECT * FROM student WHERE sno = 1)和 ( SELECT * FROM student WERE sno = sc.sno )这两个子查询都变成 LATERAL,而不是只是针对引用父查询属性子查询才会拥有LATERAL语义。

if (rte->lateral)

{

ListCell *rt;

foreach(rt, rtable)

{

RangeTblEntry *child_rte = (RangeTblEntry *) lfirst(rt);

Assert(child_rte->rtekind == RTE_SUBQUERY);

child_rte->lateral = true;

}

}

3.把第三层次的两个RangeTblEntry:(SELECT * FROM student WHERE sno = 1)和(SELECT * FROM student WHERE sno = sc.sno )两个子查询附加到第一层的 Query->rtable 列表中,在这第3步过后,后续的子查询的rtindex都将加上父查询rtindex作为偏置值。

/*

  • Append child RTEs (and their perminfos) to parent rtable.

*/

CombineRangeTables(&root->parse->rtable, &root->parse->rteperminfos,

rtable, subquery->rteperminfos);

{

*dst_rtable = list_concat(*dst_rtable, src_rtable); ...

}

4.开始对 subquery->setOperations 进行遍历 (pull_up_union_leaf_queries 函数),为其中的每个子查询生成一个AppendRelInfo 节点,在本例中为( SELECT * FROM student WHERE sno = 1〕和 (SELECT * FROM student WHERE sno = sc.sno )生成两个 AppendRelInfo 节点,这种类型的节点是记录到查询树的上下文中,在查询树中看不到。

SetOperationStmt *op = (SetOperationStmt *) setOp;

/* Recurse to reach leaf queries */

pull_up_union_leaf_queries(op->larg, root, parentRTindex, setOpQuery,

childRToffset);

pull_up_union_leaf_queries(op->rarg, root, parentRTindex, setOpQuery,

childRToffset);

appinfo = makeNode(AppendRelInfo);

appinfo->parent_relid = parentRTindex;

appinfo->child_relid = childRTindex;

appinfo->parent_reltype = InvalidOid;

appinfo->child_reltype = InvalidOid;

make_setop_translation_list(setOpQuery, childRTindex, appinfo);

appinfo->parent_reloid = InvalidOid;

root->append_rel_list = lappend(root->append_rel_list, appinfo);

简单回顾这种类型子查询流程如下图:

到此为止,还有一个需要解决的问题:子查询提升将对应的RangeTblEntry添加到了父查询的rtable中,而且过程中更新了rtindex(第4步),这个新的RangeTblEntry不会在父查询的FromExpr中出现,所以构造完ApendRelInfo后,需要对子查询构造新的RangeTblRef,填充新的rtindex, 然后执行pull_up_subqueries_recurse。

rtr = makeNode(RangeTblRef);

rtr->rtindex = childRTindex;

(void) pull_up_subqueries_recurse(root, (Node *) rtr,

NULL, appinfo);

最后就能得到优化后的查询树结构。

http://www.gsyq.cn/news/1605999.html

相关文章:

  • STM32和STM32CubeMX实现SHT30温湿度传感器 保姆级教程
  • 社区公益服务平台 Java+SpringBoot+Vue 前后分离
  • 营销智能体选哪个?一份基于实际场景的对比指南正在改变内容生成、投放优化和用户互动的效率。但市场上的产品形态差异很大:有的只是套了壳的通用写作工具,有的是传统营销 SaaS 加了个 AI 入口。选错不仅
  • 口碑佳的智能产品有何奥秘
  • 收藏!AI应用开发路线图:Java后端+Python大模型,小白也能轻松入门并快速上手
  • 软件数据可视化化的图表展示与交互
  • 暑假将至,校园安防不“放假”:国标GB28181视频监控平台EasyCVR这套视频融合方案让安全“全年无休”
  • [百度网盘] 大模型AI应用开发企业级项目实战(提示词工程+大模型NLP应用+AI对话产品)
  • 数据库巡检怎么做?Prometheus+Grafana监控体系搭建指南
  • 记一次由「系统Swap空间」被频繁使用导致的性能急剧下降
  • 软件检测实验室CMA资质认定技术人员和管理人员岗位要求与职责划分
  • GPT-5.6震撼来袭!OpenAI开启智能体基础设施时代,跑分已不重要!
  • 快速集成脑筋急转弯API:用Python构建你的命令行问答游戏
  • MSPM0 SYSCTL模块深度解析:时钟与功耗管理实战指南
  • 16 CFR 1640软垫家具阻燃
  • 从后厨到前台:一家连锁餐企如何用三年时间完成合同管理的数字化重构
  • 5款热门有声书软件实测,哪款最适合你?
  • 操作系统内存分配:伙伴系统与Slab分配器的结合
  • 【ChatGPT API成本控制实战手册】:20年架构师亲授7大隐形计费陷阱与精准预算建模法
  • 微信小程序性能优化:首屏加载与渲染提速指南
  • 20人研发团队MacBook选型找谁咨询
  • Java毕设选题推荐:基于 Java 的上下级任务对接管理平台设计与开发 轻量化企业任务审批与跟踪管理系统设计实现【附源码、mysql、文档、调试+代码讲解+全bao等】
  • 智能推荐化技术中的协同过滤内容推荐与混合推荐
  • 降重降AI工具哪个好?多款工具实测对比
  • 捷克行业市场整体发展情况解读
  • 2026年期货公司避险对冲能力深度对比:选对平台比选对手续费更重要
  • MySQL 查询优化实战记录
  • 大湾区首家突破 200 亿估值具身智能公司诞生,自变量超豪华投资阵容曝光
  • 专精特新与高新技术企业为何需要基于容度原理的颠覆性技术?
  • 本地文档处理链怎么做轻一点?从 PDF、Markdown 到 JSON 看 ZTools