半连接转内连接 | OceanBase SQL 查询改写
查询优化器是关系型数据库系统的核心模块,是数据库内核开发的重点和难点,也是衡量整个数据库系统成熟度的“试金石”。为了帮助大家更好地理解 OceanBase 查询优化器,我们撰写了查询改写系列文章,带大家更好地掌握查询改写的精髓,熟悉复杂 SQL 的等价性,写出高效的 SQL。本文将重点和大家介绍半连接转内连接,更多文章欢迎进入【OceanBase SQL 查询改写专题】 查看。

引言
查询分析中经常使用子查询语句,数据库为了提高子查询的执行性能,往往会把子查询语句改写成半连接(子查询提升方法参见本系列第二篇:子查询提升首篇)。
例如,我们需要查询2022-08-01到2022-08-02之间已排片的电影,可以通过IN子查询检查电影是否在排片期内。查询SQL如Q1所示。
-- 影片表 MOVIE(movie_id primary key, movie_name, release_date) -- 排片表 PLAY(play_id, movie_id, play_time, price, seats)Q1: SELECT movie_name FROM movie WHERE movie_id IN (SELECT movie_idFROM playWHERE play_time BETWEEN DATE'2022-08-01' AND DATE'2022-08-02');Q2: SELECT movie_name FROM movie LEFT SEMI JOIN (SELECT movie_idFROM playWHERE play_time BETWEEN date'2022-08-01' AND date'2022-08-02' )play ON movie.movie_id = play.movie_id;
对于查询Q1,OceanBase会做子查询提升改写,改写成等价的查询Q2,使用半连接来计算子查询。对于新的查询,优化器可以选择hash left semi join、hash right semi join、merge left semi join、merge right semi join、nested loop left semi join五种连接算法执行。下图展示了其中一种执行计划。
Query Plan: ========================================== |ID|OPERATOR |NAME |EST. ROWS|COST | ------------------------------------------ |0 |HASH SEMI JOIN| |30 |910000| |1 | TABLE SCAN |MOVIE|1000000 |460000| |2 | SUBPLAN SCAN |PLAY |30 |46 | |3 | TABLE SCAN |PLAY |30 |46 | ==========================================
考虑一种业务场景:movie表的数据量达100w,2022-08-01到2022-08-02之间已排片的电影约30部。上面五种连接算法都需要扫描movie表的全部数据,扫描成本比较高。而我们知道movie表的主键为movie_id,如果我们能够先查询出2022-08-01到2022-08-02之间已排片的movie_id,再去movie表查询movie_name,就能够使用movie表的主键索引,执行30次主键索引扫描即可完成查询。
为了能够按照最优计划执行Q1查询,我们需要以play表作为驱动表,并且使用index nested loop join的算法,把movie_id的连接条件转换为movie表的索引扫描条件。计划如下所示。
Query Plan: ===================================================== |ID|OPERATOR |NAME |EST. ROWS|COST| ----------------------------------------------------- |0 |NESTED LOOP RIGHT SEMI JOIN| |30 |91 | |1 | SUBPLAN SCAN |PLAY |30 |96 | |2 | TABLE SCAN |PLAY |30 |96 | |3 | TABLE GET |MOVIE|1 |46 | =====================================================
但我们知道,数据库没有NESTED LOOP RIGHT SEMI JOIN的算法实现,那我们要怎样改写这条SQL,使数据库能够支持这种优化计划呢?为此,OceanBase引入了一个改写规则:半连接转内连接,将满足一定条件的半连接查询转换为内连接查询,优化器就可以针对上述场景生成最优的index nested loop join计划。
半连接转内连接
介绍半连接转内连接规则前,我们先了解下半连接的执行逻辑。还是以Q1为例进行说明,从movie表中读取一行数据,然后从play表内查找指定movie_id的数据,如果存在,则执行数据,否则不输出。从描述中我们可以知道,对于movie表中给定的一行数据,无论play表存在多少条数据与指定的movie_id相同,查询都只输出一行数据。
而内连接对于符合条件的每一条数据都会输出,也就是说,如果半连接直接转内连接,执行结果可能会重复输出多次。为了保证改写不改变查询语义,我们需要对play的movie_id去重,保证movie表的每行数据在play表中只匹配一行数据,改写后的查询如Q3所示。
Q3: SELECT movie_name FROM movie INNER JOIN JOIN (SELECT DISTINCT movie_idFROM playWHERE play_time BETWEEN date'2022-08-01' AND date'2022-08-02' )play ON movie.movie_id = play.movie_id;
对于新的查询Q3,优化器可以尝试movie hash join play、play hash join movie、movie merge join play、 play merge join movie、movie nested loop join play、play nested loop join movie这六种连接算法执行,比原来多了一种。此时,优化器可以生成之前描述的最优计划。
================================================ |ID|OPERATOR |NAME |EST. ROWS|COST| ------------------------------------------------ |0 |NESTED-LOOP JOIN | |30 |46 | |1 | SUBPLAN SCAN |PLAY |30 |46 | |2 | MERGE DISTINCT | |30 |46 | |3 | SORT | |30 |46 | |4 | TABLE SCAN |PLAY |30 |46 | |5 | TABLE GET |MOVIE|1 |7 | ================================================
注意到改写之后的查询比原来的查询多了一次去重计算,Q3查询并不是在所有场景下都比Q2查询更优,因此,OceanBase的半连接转内连接改写是一种基于代价的改写,即优化器会对比改写前后最优计划的代价,如果代价降低了,才会应用改写,否则不会改写查询。
优化点
上文我们介绍了半连接转内连接主要是增加去重计算来保证语义的正确性,也正因为增加了去重计算,改写之后的查询并不总是比改写之前的查询更优。
我们可以思考一下,是否所有场景都需要加去重计算?答案是否定的,在有些场景下,我们可以把半连接直接转成内连接,例如:play表的movie_id本身就有唯一约束,或者play表只有一行数据满足条件。在这些场景下,我们可以不添加去重计算,这也意味着改写之后的查询总是比改写之前的查询更优,不需要额外比较代价。
改写陷阱
在之前的介绍中,我们没有说明数据类型对改写规则的影响,实际上半连接转内连接对数据类型是有要求的。通过一个例子说明,对于查询Q4,如果需要改写成内连接,改写的SQL如Q5所示。
create table t1(c1 int);
insert into t1 values(0);
create table t2(c1 varchar(20));
insert into t2 values('0.0');
insert into t2 values('0.1');Q4:
SELECT *
FROM t1
WHERE c1 IN (SELECT c1FROM t2);
Q5:
SELECT t1.c1
FROM t1INNER JOIN (SELECT DISTINCT c1FROM t2)t2ON t1.c1 = t2.c1;
上面的改写正确吗?对于Q4,结果是一行数据:0,对于Q5,结果是两行数据:0, 0。为什么呢?在对t2表的c1列去重时,使用的是varchar(20)类型,'0.0'与'0.1'属于不同的数据,不会发生去重操作,与t1表连接时需要把varchar(20)类型的数据转换成int类型比较,此时'0.0'与'0.1'转换成了0与0,导致执行结果不正确。
为了避免数据类型影响改写的正确性,我们需要在改写时,对数据类型做适当的处理,你可以思考一下怎样是正确的改写查询。
总结
本文主要介绍OceanBase的半连接转内连接改写,以及这个改写的优化点、容易被忽略的错误。OceanBase会把满足一定条件的半连接转换成内连接,使优化器能够尝试更多的计划,生成的查询计划可能更优。
相关文章:
半连接转内连接 | OceanBase SQL 查询改写
查询优化器是关系型数据库系统的核心模块,是数据库内核开发的重点和难点,也是衡量整个数据库系统成熟度的“试金石”。为了帮助大家更好地理解 OceanBase 查询优化器,我们撰写了查询改写系列文章,带大家更好地掌握查询改写的精髓&…...
Git使用经历
目录 1、先创建文件夹 2、仓库初始化 3、配置gitee用户名和密码 4、克隆指定仓库的中指定分支到本地仓库 5、查看当前所在分支、切换分支 6、查看状态,判断是否有修改 7、把更新的内容添加到缓存区 8、把缓存区的数据提交 9、把数据推送到远程仓库 10、把…...
永磁同步电机控制算法-自适应带宽LADRC转速控制器
一、原理介绍 设计了自适应带宽 LADRC 控制方法,继承了 LADRC 优点的同时,加入自适应带宽控制,提出运用 Softsign 函数设计带宽自适应函数,根据电机转速自动调节控制带宽,解决了永磁同步电机在复杂且多变的环境下受到…...
基于springboot+vue实现的博物馆游客预约系统 (源码+L文+ppt)4-127
摘 要 旅游行业的快速发展使得博物馆游客预约系统成为了一个必不可少的工具。基于Java的博物馆游客预约系统旨在提供高效、准确和便捷的适用博物馆游客预约服务。本文讲述了基于java语言开发,后台数据库选择MySQL进行数据的存储。该软件的主要功能是进行博物馆游客…...
LeetCode 1705.吃苹果的最大数目:贪心(优先队列) - 清晰题解
【LetMeFly】1705.吃苹果的最大数目:贪心(优先队列) - 清晰题解 力扣题目链接:https://leetcode.cn/problems/maximum-number-of-eaten-apples/ 有一棵特殊的苹果树,一连 n 天,每天都可以长出若干个苹果。在第 i 天,…...
vim多窗格
vim打开文件分为三个阶段:buffer、window与tab buffer就是在同一个界面打开的文件window就是使用水平分割与垂直分割的窗口tab则是可以是上述两者的总集合 buffer :e filename在已打开文件的界面中再打开一个新文件,显示这个新文件,原文件被隐…...
ubuntu paddle ocr 部署bug问题解决
ubuntu paddle ocr 部署会出现异常报错。 尝试安装以下版本: pip install paddlepaddle2.5.2 -i https://pypi.tuna.tsinghua.edu.cn/simpl 助力快速掌握数据集的信息和使用方式。 数据可以如此美好!...
OpenFeign快速入门 示例:黑马商城
使用起因 之前我们利用了Nacos实现了服务的治理,利用RestTemplate实现了服务的远程调用。这样一来购物车虽然通过远程调用实现了调用商品服务的方法,但是远程调用的代码太复杂了: 解决方法 并且这种调用方式比较复杂,一会儿远程调用,一会儿本地调用。 因…...
【C++】ceil 和 floor 函数的实现与分析
博客主页: [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: C 文章目录 💯前言💯ceil 和 floor 函数的基础介绍1. ceil 函数定义与功能示例代码输出结果功能分析使用场景 2. floor 函数定义与功能示例代码输出结果功能分析使用场景 💯自行实现…...
zabbix监控山石系列Hillstone配置模版(适用于zabbix6及以上)
监控项: 触发器: 监控数据:...
在瑞芯微RK3588平台上使用RKNN部署YOLOv8Pose模型的C++实战指南
在人工智能和计算机视觉领域,人体姿态估计是一项极具挑战性的任务,它对于理解人类行为、增强人机交互等方面具有重要意义。YOLOv8Pose作为YOLO系列中的新成员,以其高效和准确性在人体姿态估计任务中脱颖而出。本文将详细介绍如何在瑞芯微RK3588平台上,使用RKNN(Rockchip N…...
CTFHub disable_functions通关
LD_PRELOAD 来到首页发现有一句话直接就可以用蚁剑连接 根目录里有/flag但是不能看;命令也被ban了就需要绕过了 绕过工具在插件市场就可以下载 如果进不去的话 项目地址: #本地仓库;插件存放 antSword\antData\plugins 绕过选择 上传后我们点进去可以看到多了一个绕过的文件;…...
Chromium GN 目标指南 - view_example 计数器示例 (七)
1. 引言 在前面的文章中,我们学习了如何在 views_examples 中添加自定义 Button 示例。在本篇文章中,我们将继续探索 Views 框架的应用,创建一个简单的计数器示例,以学习如何使用 Label 和 Button 控件进行交互,以及如…...
一步一步写线程之十六线程的安全退出之二例程
一、说明 在一篇分析了多线程的安全退出的相关机制和方式,那么本篇就针对前一篇的相关的分析进行举例分析。因为有些方法实现的方法类似,可能就不一一重复列举了,相关的例程主要以在Linux上的运行为主。 二、实例 线程间的同步,…...
【Linux系列】Shell 脚本中的条件判断:`[ ]`与`[[ ]]`的比较
💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…...
ArcGIS+MIKE21 洪水淹没分析、溃坝分析,洪水淹没动态效果
洪水淹没分析过程: 一、所需数据: 1.分析区域DEM数据 二、ArcGIS软件 1.提取分析区域DEM(水库坝下区域) 2.DEM栅格转点 3.计算转换后几何点的x和y坐标值(精度20、小数位3) 4.导出属性表,形式…...
Git 的基本概念和使用
Git是一个分布式版本控制系统,它可以帮助开发人员追踪和管理代码的修改。下面是Git的基本概念和使用方式的解释: 仓库(Repository):Git使用仓库来存储代码和版本历史记录。仓库可以位于本地计算机上,也可以…...
*【每日一题 基础题】 [蓝桥杯 2024 省 B] 好数
[蓝桥杯 2024 省 B] 好数 好数 一个整数如果按从低位到高位的顺序,奇数位(个位、百位、万位……)上的数字是奇数,偶数位(十位、千位、十万位……)上的数字是偶数,我们就称之为“好数”。 给定一…...
对中文汉字排序的方法总结
写在前面 在各个系统中,都随处可见根据某个字段进行升序(ASC)或降序(DESC)进行排序展示。但进行中文汉字排序和查找的时候,对中文汉字的排序和查找结果往往都是错误的。 为了尽量提供全面的解决方法,本文会从各个层面出发告知有需要的人对应…...
【解决报错】AttributeError: ‘NoneType‘ object has no attribute ‘group‘
学习爬虫时,遇到如下报错: 报错原因: 正则表达式的 search 或 finditer 方法没有找到任何匹配项,可能是换行符处理不当等。 解决方法如下: 在正则表达式末尾加上re.S即可,re.S是一个编译标志,…...
【Axure高保真原型】引导弹窗
今天和大家中分享引导弹窗的原型模板,载入页面后,会显示引导弹窗,适用于引导用户使用页面,点击完成后,会显示下一个引导弹窗,直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…...
linux之kylin系统nginx的安装
一、nginx的作用 1.可做高性能的web服务器 直接处理静态资源(HTML/CSS/图片等),响应速度远超传统服务器类似apache支持高并发连接 2.反向代理服务器 隐藏后端服务器IP地址,提高安全性 3.负载均衡服务器 支持多种策略分发流量…...
8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂
蛋白质结合剂(如抗体、抑制肽)在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上,高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术,但这类方法普遍面临资源消耗巨大、研发周期冗长…...
Opencv中的addweighted函数
一.addweighted函数作用 addweighted()是OpenCV库中用于图像处理的函数,主要功能是将两个输入图像(尺寸和类型相同)按照指定的权重进行加权叠加(图像融合),并添加一个标量值&#x…...
Spring Boot+Neo4j知识图谱实战:3步搭建智能关系网络!
一、引言 在数据驱动的背景下,知识图谱凭借其高效的信息组织能力,正逐步成为各行业应用的关键技术。本文聚焦 Spring Boot与Neo4j图数据库的技术结合,探讨知识图谱开发的实现细节,帮助读者掌握该技术栈在实际项目中的落地方法。 …...
OpenLayers 分屏对比(地图联动)
注:当前使用的是 ol 5.3.0 版本,天地图使用的key请到天地图官网申请,并替换为自己的key 地图分屏对比在WebGIS开发中是很常见的功能,和卷帘图层不一样的是,分屏对比是在各个地图中添加相同或者不同的图层进行对比查看。…...
Mac下Android Studio扫描根目录卡死问题记录
环境信息 操作系统: macOS 15.5 (Apple M2芯片)Android Studio版本: Meerkat Feature Drop | 2024.3.2 Patch 1 (Build #AI-243.26053.27.2432.13536105, 2025年5月22日构建) 问题现象 在项目开发过程中,提示一个依赖外部头文件的cpp源文件需要同步,点…...
Webpack性能优化:构建速度与体积优化策略
一、构建速度优化 1、升级Webpack和Node.js 优化效果:Webpack 4比Webpack 3构建时间降低60%-98%。原因: V8引擎优化(for of替代forEach、Map/Set替代Object)。默认使用更快的md4哈希算法。AST直接从Loa…...
Unity UGUI Button事件流程
场景结构 测试代码 public class TestBtn : MonoBehaviour {void Start(){var btn GetComponent<Button>();btn.onClick.AddListener(OnClick);}private void OnClick(){Debug.Log("666");}}当添加事件时 // 实例化一个ButtonClickedEvent的事件 [Formerl…...
关于easyexcel动态下拉选问题处理
前些日子突然碰到一个问题,说是客户的导入文件模版想支持部分导入内容的下拉选,于是我就找了easyexcel官网寻找解决方案,并没有找到合适的方案,没办法只能自己动手并分享出来,针对Java生成Excel下拉菜单时因选项过多导…...
