Postgresql源码(142)子查询提升pull_up_sublinks
1 案例
drop table t_fun01;
create table t_fun01 (image_id numeric primary key, content_id varchar(50), file_code varchar(20));
create index idx3 on t_fun01(content_id);
create index idx4 on t_fun01(file_code);
insert into t_fun01 select t.i, t.i%10, t.i%100 from generate_series(1,1000000) t(i);
analyze t_fun01;
explain analyze update t_fun01 t set content_id = 'null' where t.image_id in (select max(image_id) from t_fun01 where file_code = '1');
从计划上看,nestloop挂子查询Index Scan Backward确实是能想到的最优的计划:
postgres=# explain analyze update t_fun01 t set content_id = 'null' where t.image_id in (select max(image_id) from t_fun01 where file_code = '1');QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------------------------------------------------Update on t_fun01 t (cost=4.69..12.73 rows=0 width=0) (actual time=0.257..0.259 rows=0 loops=1)-> Nested Loop (cost=4.69..12.73 rows=1 width=180) (actual time=0.135..0.138 rows=1 loops=1)-> Subquery Scan on "ANY_subquery" (cost=4.27..4.29 rows=1 width=88) (actual time=0.117..0.119 rows=1 loops=1)-> Result (cost=4.27..4.28 rows=1 width=32) (actual time=0.112..0.113 rows=1 loops=1)InitPlan 1 (returns $0)-> Limit (cost=0.42..4.27 rows=1 width=6) (actual time=0.107..0.108 rows=1 loops=1)-> Index Scan Backward using t_fun01_pkey on t_fun01 (cost=0.42..36389.43 rows=9467 width=6) (actual time=0.106..0.106 rows=1 loops=1)Index Cond: (image_id IS NOT NULL)Filter: ((file_code)::text = '1'::text)Rows Removed by Filter: 99-> Index Scan using t_fun01_pkey on t_fun01 t (cost=0.42..8.44 rows=1 width=12) (actual time=0.014..0.014 rows=1 loops=1)Index Cond: (image_id = "ANY_subquery".max)Planning Time: 0.532 msExecution Time: 0.345 ms
(14 rows
优化器是如何上拉子查询的呢?
2 上拉子查询流程分析
2.1 pull_up_sublinks
pull_up_sublinks是优化器中用于子链接(SubLink)上拉优化的核心函数。- 通过递归处理查询树中的子链接(如
ANY、EXISTS),将其转换为**半连接(Semi Join)或反半连接(Anti Join)**结构,从而提升查询性能。
在当前案例中,pull_up_sublinks函数拿到的Query树如下:

pull_up_sublinks递归入口:
- 调用
pull_up_sublinks_jointree_recurse遍历jointree,处理RangeTblRef(单表)、FromExpr(FROM列表)和JoinExpr(JOIN操作)中的子链接。 - 调用
pull_up_sublinks_qual_recurse处理条件表达式中的子链接,如WHERE col = ANY (SELECT ...)。
子链接转换逻辑:
- ANY 子链接:通过
convert_ANY_sublink_to_join转换为 Semi Join,生成新的连接条件(如col = t2.key) - EXISTS 子链接:通过
convert_EXISTS_sublink_to_join转换为 Anti Join,优化为外连接形式
2.2 convert_ANY_sublink_to_join
(gdb) bt
#0 convert_ANY_sublink_to_join (root=0x2b64f60, sublink=0x2a6f848, available_rels=0x2b653c0) at subselect.c:1272
#1 0x00000000008a1606 in pull_up_sublinks_qual_recurse (root=0x2b64f60, node=0x2a6f848, jtlink1=0x7ffd2e7e8318, available_rels1=0x2b653c0, jtlink2=0x0, available_rels2=0x0)at prepjointree.c:508
#2 0x00000000008a12e1 in pull_up_sublinks_jointree_recurse (root=0x2b64f60, jtnode=0x2b64cb0, relids=0x7ffd2e7e8380) at prepjointree.c:379
#3 0x00000000008a10e4 in pull_up_sublinks (root=0x2b64f60) at prepjointree.c:315
#4 0x0000000000886ad1 in subquery_planner (glob=0x2a6f968, parse=0x2a6fa78, parent_root=0x0, hasRecursion=false, tuple_fraction=0) at planner.c:689
#5 0x0000000000885e98 in standard_planner (parse=0x2a6fa78,query_string=0x2a6e538 "update t_fun01 t set content_id = 'null' where t.image_id in (select max(image_id) from t_fun01 where file_code = '1');", cursorOptions=2048,boundParams=0x0) at planner.c:413
进入convert_ANY_sublink_to_join时available_rels=[1,0]指向t_image,拿到的sublink:

convert_ANY_sublink_to_join
- 输入:调用者已在查询的某个条件子句(qual clause)顶层发现了一个 ANY 子链接,但尚未进一步检查该子链接的属性。
- 功能:判断是否适合以连接方式处理此子链接。若适合,构造并返回一个 JoinExpr 结构;否则返回 NULL(表示无法转换)。
- 关键限制参数:available_rels
- 含义:允许在子链接表达式中安全引用的父查询关系集合(即表或视图的标识集合)。
- 作用:避免当子链接出现在外连接(Outer Join)的 ON 条件中时,因引用非法关系而改变查询语义。
- 强制要求:若转换后的条件引用了 available_rels 之外的父查询关系,则转换必须失败。
JoinExpr *
convert_ANY_sublink_to_join(PlannerInfo *root, SubLink *sublink,Relids available_rels)
第一步判断:pull_varnos拿到 子查询里面 引用了那些level=0的基表,这里upper_varnos只有一个元素1。因为只用了一个RTE:t_fun01
upper_varnos = pull_varnos(root, sublink->testexpr);if (bms_is_empty(upper_varnos))return NULL;
第二步判断:不能引用available_rels之外的表。
if (!bms_is_subset(upper_varnos, available_rels))return NULL;
第三步判断:不能带v类型的函数。
if (contain_volatile_functions(sublink->testexpr))return NULL;
到这里就说明可以转,开始拼join,增加一个RTE叫做ANY_subquery,所以可以在计划里面看到这个名字。
pstate = make_parsestate(NULL);nsitem = addRangeTableEntryForSubquery(pstate,subselect,makeAlias("ANY_subquery", NIL),false,false);rte = nsitem->p_rte;parse->rtable = lappend(parse->rtable, rte);rtindex = list_length(parse->rtable);rtr = makeNode(RangeTblRef);rtr->rtindex = rtindex;subquery_vars = generate_subquery_vars(root,subselect->targetList,rtindex);quals = convert_testexpr(root, sublink->testexpr, subquery_vars);result = makeNode(JoinExpr);result->jointype = JOIN_SEMI;result->isNatural = false;result->larg = NULL; /* caller must fill this in */result->rarg = (Node *) rtr;result->usingClause = NIL;result->join_using_alias = NULL;result->quals = quals;result->alias = NULL;result->rtindex = 0;return result;
3 pull_varnos
功能
- 工具函数,其核心作用是 从语法树中提取所有引用“零层级”基表RTE的变量编号varnos集合。
- varno:变量编号,唯一标识查询中引用的基表(如 FROM 后的表、子查询、连接结果等)。
- 零层级(level-zero):指当前查询层级的基表(不包含嵌套子查询中的表)。
- nullingrels:外连接中可能导致列值为 NULL 的基表集合(如 LEFT JOIN 的右表)。
工作方式:
- query_or_expression_tree_walker递归遍历语法树,调用 pull_varnos_walker 处理每个节点
- Var 节点:提取其 varno 和 varnullingrels(外连接空值关系)。
- PlaceHolderVar 节点:若 root 非空,提取其 phnullingrels。
- SubLink 节点:递归处理子链接内的表达式,捕捉上层的 varno 引用。
- SubPlan 节点:仅处理传递给子计划的参数(子计划内部已独立优化)。
层级控制
- sublevels_up 计数器:跟踪嵌套子查询的层级。
- 当进入子查询时,计数器递增,仅收集当前层级(零层级)的 varno。
- 嵌套子查询中的 varno 会被忽略(因其属于深层级)。
Relids
pull_varnos(PlannerInfo *root, Node *node)
{pull_varnos_context context;context.varnos = NULL;context.root = root;context.sublevels_up = 0;/** Must be prepared to start with a Query or a bare expression tree; if* it's a Query, we don't want to increment sublevels_up.*/query_or_expression_tree_walker(node,pull_varnos_walker,(void *) &context,0);return context.varnos;
}
相关文章:
Postgresql源码(142)子查询提升pull_up_sublinks
1 案例 drop table t_fun01; create table t_fun01 (image_id numeric primary key, content_id varchar(50), file_code varchar(20)); create index idx3 on t_fun01(content_id); create index idx4 on t_fun01(file_code); insert into t_fun01 select t.i, t.i%10, t.i%1…...
通过 ECNWP 洋流、海浪可视化开发的方法和架构
第一篇:ECNWP 洋流、海浪可视化开发方法 1. 数据获取与预处理 ECNWP(Eastern Central North Pacific)区域的洋流和海浪数据通常来源于 NOAA、EMC 等权威机构。数据包括流速、流向、波高、波向、周期等。 代码示例:数据下载与预处理 import pandas as pd import numpy as…...
如何 编译 px4
DeepSeek-R1满血版 回答完成 深度思考完成 用时27秒,搜索全网29篇资料 以下是详细的PX4固件编译教程,综合多平台及常见问题解决方案: 一、环境准备 系统要求 Ubuntu 20.04/22.04(推荐)或 MacOS 12 Windo…...
23种设计模式-桥接(Bridge)设计模式
桥接设计模式 🚩什么是桥接设计模式?🚩桥接设计模式的特点🚩桥接设计模式的结构🚩桥接设计模式的优缺点🚩桥接设计模式的Java实现🚩代码总结🚩总结 🚩什么是桥接设计模式…...
【黑皮书】 AVL树
目录 前言 一 AVL树的介绍 二 单旋转 二 双旋转 总结 前言 AVL树的学习 一 AVL树的介绍 AVL树是带有平衡条件的二叉查找树,这个平衡条件要持续保持,而且必须保证树的深度为O(log(N))最简单的想法就是要求左右子树具有相同的高度 一棵AVL树是…...
【机器学习】什么是决策树?
什么是决策树? 决策树是一种用于分类和回归问题的模型。它通过一系列的“决策”将数据逐步分裂,最终得出预测结果。可以把它看作是一个“树”,每个节点表示一个特征的判断,而每个分支代表了可能的判断结果,最终的叶子…...
【商城实战(74)】数据采集与整理,夯实电商运营基石
【商城实战】专栏重磅来袭!这是一份专为开发者与电商从业者打造的超详细指南。从项目基础搭建,运用 uniapp、Element Plus、SpringBoot 搭建商城框架,到用户、商品、订单等核心模块开发,再到性能优化、安全加固、多端适配…...
使用独立服务器的最佳方式指南
在寻找合适的主机服务方案时,可以考虑独立服务器,因为它拥有管理员权限以及更高的性能配置。在本指南中,我们将介绍独立服务器的多种用途,并分析为什么选择独立服务器可能是处理高性能、资源密集型应用和大流量网站的最佳方案。 搭…...
视频格式转换:畅享多平台无缝视频体验
视频格式转换:畅享多平台无缝视频体验 视频已成为我们日常生活中不可或缺的一部分,不论是工作中展示方案的演示,还是生活里记录美好瞬间的短片,视频的存在无处不在。然而,面对各类设备、平台对视频格式的不同要求&…...
【HTML 基础教程】HTML 属性
HTML 属性 属性是 HTML 元素提供的附加信息。 属性通常出现在 HTML 标签的开始标签中,用于定义元素的行为、样式、内容或其他特性。 属性总是以 name"value" 的形式写在标签内,name 是属性的名称,value 是属性的值。 HTML 属性 …...
爬虫问题整理(2025.3.27)
此时此刻,困扰我一天的两个问题终于得到了解决,在此分享给大家。 问题1:使用anaconda prompt无法进行pip安装,这里只是一个示例,实际安装任何模块都会出现类似报错。 解决办法:关掉梯子......没错…...
短信验证码安全需求设计
背景: 近期发现部分系统再短信充值频繁,发现存在恶意消耗短信额度现象,数据库表排查,发现大量非合法用户非法调用短信接口API导致额度耗尽。由于系统当初设计存在安全缺陷,故被不法分子进行利用,造成损失。…...
若依专题——基础应用篇
若依搭建 搭建后端项目 ① Git 克隆并初始化项目 ② MySQL 导入与配置 ③ 启动 Redis 搭建后端项目注意事项? ① 项目初始化慢,执行clean、package ② MySQL导入后,修改application-druid.yml ③ Redis有密码,修改ap…...
给AI装“记忆U盘“:LangChain记忆持久化入门指南
🧠 什么是记忆持久化? 想象AI对话就像和朋友聊天: 普通模式:每次重启都忘记之前聊过什么持久化模式:给AI配了个"记忆U盘",聊天记录永不丢失 核心组件三件套 #mermaid-svg-ORm8cbBXsaRy2sZ…...
AI for CFD入门指南(传承版)
AI for CFD入门指南 前言适用对象核心目标基础准备传承机制 AI for CFDLibtorch的介绍与使用方法PytorchAutogluon MakefileVscodeOpenFOAMParaviewGambit 前言 适用对象 新加入课题组的硕士/博士研究生对AICFD交叉领域感兴趣的本科生实习生需要快速上手组内研究工具的合作研…...
DeepSeek+RAG局域网部署
已经有很多平台集成RAG模式,dify,cherrystudio等,这里通过AI辅助,用DS的API实现一个简单的RAG部署。框架主要技术栈是Chroma,langchain,streamlit,答案流式输出,并且对答案加上索引。支持doc,docx,pdf,txt。…...
JavaScript快速入门之函数
引言 总所周知,JavaScript是一个很随便的语言,因此,在学习它的语法的时候,我是和Java语法对比着学的,可能会有些绕 函数 方法:对象(属性,方法) 函数:放在对…...
Java中synchronized 和 Lock
1. synchronized 关键字 工作原理 对象锁:在Java中,每个对象都有一个与之关联的监视器锁(monitor lock)。当一个线程尝试进入由 synchronized 保护的代码块或方法时,它必须首先获取该对象的监视器锁。如果锁已经被其…...
Linux系统-ls命令
一、ls命令的定义 Linux ls命令(英文全拼:list directory contents)用于显示指定工作目录下之内容(列出目前工作目录所含的文件及子目录)。 二、ls命令的语法 ls [选项] [目录或文件名] ls [-alrtAFR] [name...] 三、参数[选项…...
个人学习编程(3-24) 数据结构
括号的匹配: if((s[i]) && now() || (s[i]] && now[)){ #include <bits/stdc.h>using namespace std;int main() {char s[300];scanf("%s",&s);int i;int len strlen(s);stack <char> st;for (i 0; i < len; i){if(…...
.NET开源的智能体相关项目推荐
一、AntSK 由AIDotNet团队开发的人工智能知识库与智能体框架,支持多模型集成和离线部署能力。 核心能力: • 支持OpenAI、Azure OpenAI、星火、阿里灵积等主流大模型,以及20余种国产数据库(如达梦) • 内置语义内核&a…...
面试八股文--框架篇(SSM)
一、Spring框架 1、什么是spring Spring框架是一个开源的Java平台应用程序框架,由Rod Johnson于2003年首次发布。它提供了一种全面的编程和配置模型,用于构建现代化的基于Java的企业应用程序。Spring框架的核心特性包括依赖注入(DI…...
跨语言语言模型预训练
摘要 最近的研究表明,生成式预训练在英语自然语言理解任务中表现出较高的效率。在本研究中,我们将这一方法扩展到多种语言,并展示跨语言预训练的有效性。我们提出了两种学习跨语言语言模型(XLM)的方法:一种…...
[识记]Mysql8 远程授权
今天在测试docker时,因更换为Mysql8,使用SQL方式实现远程授权,其方式方法同于Mysql,但语句稍有不同,仅供参考。 登录mysql mysql -u root -p 输入密码: [请依据交互输入你的mysql密码]切换数据库 use mysql;选择需要…...
Nodejs上传文件的问题
操作系统:window和linux都会遇到 软件环境:v20.10.0的Nodejs 1、前端代码如下: 2、后端Nodejs 2.1、注册接口 2.2、上传接口 其中memoryUpload方法代码如下: 3、用页面上传文件 查看具体报错原因: TypeError: sourc…...
无人机螺旋桨平衡标准
螺旋桨平衡是确保无人机(UAV)平稳运行、可靠性和使用寿命的关键过程。螺旋桨的不平衡会导致振动、噪音,并加速关键部件的磨损,从而对飞行性能产生负面影响。 ISO 21940-11:2016标准为旋翼平衡提供了一个广泛引用的框架,定义了可接受的不平衡…...
Qt开发:双缓冲机制
文章目录 什么是双缓冲机制?Qt 中的双缓冲实现方式 什么是双缓冲机制? 双缓冲(Double Buffering) 是一种减少屏幕绘制闪烁的技术,广泛用于图形渲染和游戏开发。 它的基本原理是: 先在后台缓冲区࿰…...
HandyJSON原理
HandyJSON 的优势 JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式, 应用广泛. 在 App 的使用过程中, 服务端给移动端发送的大部分都是 JSON 数据, 移动端需要解析数据才能做进一步的处理. 在解析JSON数据这一块, 目前 Swift 中流行的框架基本上是 SwiftyJSON, …...
SpringBoot+策略模式+枚举类,使用配置文件改进,优雅消除if-else,完全符合OOP原则
需求分析 公司做物联网系统的,使用nettry进行设备连接,对设备进行数据采集,根据设备的协议对数据进行解析,解析完成之后存放数据库,但是不同厂家的设备协议不同。公司系统使用了使用了函数式编程的去写了一个解析类&am…...
[力扣每日一练]关于MySQL和pandas的正则表达式应用
一:题目要求 表:Users-------------------------- | Column Name | Type | -------------------------- | user_id | int | | email | varchar | -------------------------- (user_id) 是这张表的唯一主键。 每一行包含用…...
