多表关联查询过滤条件写在on与where后的区别
wptr33 2024-12-26 17:07 42 浏览
SQL优化过程中,发现开发人员在写多表关联查询的时候,对于谓词过滤条件的写法很随意,写在on后面与where后面的情况均有,这可能会导致没有理解清楚其真正的含义而无法得到期望的结果。
多表关联连接方式有inner join、left join、right join、full join四种,下面通过实验来说明不同连接方式谓词放在on与where后的效果与影响。
初始化测试数据
create table t1(id number(10),name varchar2(30),status varchar2(2));
create table t2(id number(10),mobile varchar2(30));
insert into t1 values(1,'a','1');
insert into t1 values(2,'b','1');
insert into t1 values(3,'c','1');
insert into t1 values(4,'d','1');
insert into t1 values(5,'e','1');
insert into t1 values(6,'f','0');
insert into t1 values(7,'g','0');
insert into t1 values(8,'h','0');
insert into t1 values(9,'i','0');
insert into t1 values(10,'j','0');
insert into t2 values(1,'12345');
insert into t2 values(2,'23456');
insert into t2 values(3,'34567');
insert into t2 values(6,'67890');
insert into t2 values(7,'78901');
1.Inner join
SQL>select * from t1 inner join t2 on t1.id=t2.id and t1.status=‘1’;
ID NAME ST ID MOBILE
1 a 1 1 12345
2 b 1 2 23456
3 c 1 3 34567
SQL> select * from t1 inner join t2 on t1.id=t2.id where t1.status=‘1’;
ID NAME ST ID MOBILE
1 a 1 1 12345
2 b 1 2 23456
3 c 1 3 34567
我们发现谓词t1.status=’1’放在on后与where后结果一样,它们的执行计划相同,说明CBO对这两种情况做了相同处理。
执行计划如下图所示:
Inner join时谓词不管放在哪个位置,CBO都先对t1表过滤,再与t2表关联。
2.left join
(1)左右表谓词过滤都放在on后面:
SQL> select * from t1 left join t2 on t1.id=t2.id and t1.status=‘1’ and t2.id<3;
ID NAME ST ID MOBILE
1 a 1 1 12345
2 b 1 2 23456
3 c 1
8 h 0
5 e 1
9 i 0
10 j 0
7 g 0
6 f 0
4 d 1
执行计划如下:
从执行计划可以看出,t1.status=’1’放在on后面,t1表并没有对谓词status进行过滤,结果集显示t1的全表数据。这是由left join的特性决定的,左表会显示全部数据。t2.id<3是先对t2表进行过滤再进行连接,而t1.status=’1’是作为连接条件存在,对连接时产生的笛卡尔积数据做连接过滤。
(2)左右表谓词过滤都放在where后面:
SQL>select * from t1 left join t2 on t1.id=t2.id where t1.status=‘1’ and t2.id<3;
ID NAME ST ID MOBILE
1 a 1 1 12345
2 b 1 2 23456
从执行计划可以看出,谓词放在where后面,是先对表进行过滤,然后再对过滤后的数据进行连接。而且我们发现t1表上自动加上了id<3的过滤条件,这是因为有t1.id=t2.id等值连接,如果t1表上id列有索引,性能就能看出差别来了。注意连接方式变成了hash join,这是因为右表的谓词过滤条件写在where后面,CBO会把左连接等价为内连接。
(3)右表的谓词写在on后面,左表的谓词写在where后面:
SQL>select * from t1 left join t2 on t1.id=t2.id and t2.id<3
where t1.status=‘1’; 2
ID NAME ST ID MOBILE
1 a 1 1 12345
2 b 1 2 23456
5 e 1
4 d 1
3 c 1
当把对右表的过滤写在on后面,先对两表进行过滤,再进行left join,显示结果集与写在where后面是不同的,连接方式还是左外连接,显示t1过滤后的全部数据。
(4)右表的谓词写在where后面,左表的谓词写在on后面:
SQL> select * from t1 left join t2 on t1.id=t2.id and t1.status=‘1’ where t2.id<7;
ID NAME ST ID MOBILE
1 a 1 1 12345
2 b 1 2 23456
3 c 1 3 34567
从执行计划看这种情况左连接转换为内连接,左表的谓词条件写在哪个位置都一样。而且因为t2表过滤后数据比t1表少,CBO把t2表当成了驱动表。
接下来我们再看一个语句:
SQL> select * from t1 left join t2 on t1.id=t2.id and t1.status=‘1’
where t1.status=‘0’ ;
ID NAME ST ID MOBILE
8 h 0
6 f 0
9 i 0
10 j 0
7 g 0
从执行计划看出,虽然t2表返回0行,步骤3上的filter条件肯定不成立,但有逻辑读消耗,所以推断它依然进行了全表扫描,所以这种语句对t2表的扫描是对资源的一种浪费,没有意义。或许你会觉得谁会这么无聊写这种SQL,但是在开发过程中,SQL语句经常是各种过滤条件组合经过拼接而成,因为返回结果是对的,他们意识不到会出现这种问题,在此说明此种情况主要是想说明一件事:不要总想着用一个语句来解决所有的功能需求,适当的拆分对性能的提升是很有必要的。
3.right join
右连接与左连接是相似的,只不过是右表显示全部数据,写在on后面谓词过滤对右表不起作用,在此不再举例说明。
4.full join
全连接在应用中似乎很少碰到,但是存在即合理,只是自己没有遇到而已。
(1)两个表的谓词都放在on的后面:
这种情况不会先对两个表过滤,而是作为连接条件过滤,符合连接就匹配上,不符合的就把左右两表的数据都显示出来,另一表的字段以空显示。
(2)两个表的谓词都放在where后面:
这种情况CBO将其转换为内连接,先过滤再关联。
(3)左表谓词放在on后面,右表放在where后面:
这种情况转换为右外连接,但是也是先对两表过滤后再关联。
(4)左表谓词放在where后面,右表放在on后面:
这种情况转换为左外连接,也是先对两表过滤后再关联。
总结
1.对于内连接inner join,两个表的谓词条件放在on与where后面相同。
2.对于left join:
左表谓词放在on后不会对左表数据进行过滤,依然显示左表全部数据,放在where后面才会对左表进行过滤
右表谓词不管放在on后还是where后都会对右表先过滤再连接,但是放在where后left join会转换为inner join。
3.对于外连接,谓词条件放的位置不同,结果集也不同,可以根据自己的需求斟酌使用。
墨天轮原文链接:https://www.modb.pro/db/101411(复制链接至浏览器或点击多表关联查询过滤条件写在on与where后的区别 - 墨天轮查看)
关于作者
于志君,云和恩墨技术顾问,Oracle 12c OCM。拥有OCM、OBCA证书,曾在某大型国企做过多年数据库运维工作,现驻场于某银行,专门从事SQL性能优化工作,热衷于运维故障处理、备份恢复、升级迁移、性能优化的学习与分享。
相关推荐
- redis的八种使用场景
-
前言:redis是我们工作开发中,经常要打交道的,下面对redis的使用场景做总结介绍也是对redis举报的功能做梳理。缓存Redis最常见的用途是作为缓存,用于加速应用程序的响应速度。...
- 基于Redis的3种分布式ID生成策略
-
在分布式系统设计中,全局唯一ID是一个基础而关键的组件。随着业务规模扩大和系统架构向微服务演进,传统的单机自增ID已无法满足需求。高并发、高可用的分布式ID生成方案成为构建可靠分布式系统的必要条件。R...
- 基于OpenWrt系统路由器的模式切换与网页设计
-
摘要:目前商用WiFi路由器已应用到多个领域,商家通过给用户提供一个稳定免费WiFi热点达到吸引客户、提升服务的目标。传统路由器自带的Luci界面提供了工厂模式的Web界面,用户可通过该界面配置路...
- 这篇文章教你看明白 nginx-ingress 控制器
-
主机nginx一般nginx做主机反向代理(网关)有以下配置...
- 如何用redis实现注册中心
-
一句话总结使用Redis实现注册中心:服务注册...
- 爱可可老师24小时热门分享(2020.5.10)
-
No1.看自己以前写的代码是种什么体验?No2.DooM-chip!国外网友SylvainLefebvre自制的无CPU、无操作码、无指令计数器...No3.我认为CS学位可以更好,如...
- Apportable:拯救程序员,IOS一秒变安卓
-
摘要:还在为了跨平台使用cocos2d-x吗,拯救objc程序员的奇葩来了,ApportableSDK:FreeAndroidsupportforcocos2d-iPhone。App...
- JAVA实现超买超卖方案汇总,那个最适合你,一篇文章彻底讲透
-
以下是几种Java实现超买超卖问题的核心解决方案及代码示例,针对高并发场景下的库存扣减问题:方案一:Redis原子操作+Lua脚本(推荐)//使用Redis+Lua保证原子性publicbo...
- 3月26日更新 快速施法自动施法可独立设置
-
2016年3月26日DOTA2有一个79.6MB的更新主要是针对自动施法和快速施法的调整本来内容不多不少朋友都有自动施法和快速施法的困扰英文更新日志一些视觉BUG修复就不翻译了主要翻译自动施...
- Redis 是如何提供服务的
-
在刚刚接触Redis的时候,最想要知道的是一个’setnameJhon’命令到达Redis服务器的时候,它是如何返回’OK’的?里面命令处理的流程如何,具体细节怎么样?你一定有问过自己...
- lua _G、_VERSION使用
-
到这里我们已经把lua基础库中的函数介绍完了,除了函数外基础库中还有两个常量,一个是_G,另一个是_VERSION。_G是基础库本身,指向自己,这个变量很有意思,可以无限引用自己,最后得到的还是自己,...
- China's top diplomat to chair third China-Pacific Island countries foreign ministers' meeting
-
BEIJING,May21(Xinhua)--ChineseForeignMinisterWangYi,alsoamemberofthePoliticalBureau...
- 移动工作交流工具Lua推出Insights数据分析产品
-
Lua是一个适用于各种职业人士的移动交流平台,它在今天推出了一项叫做Insights的全新功能。Insights是一个数据平台,客户可以在上面实时看到员工之间的交流情况,并分析这些情况对公司发展的影响...
- Redis 7新武器:用Redis Stack实现向量搜索的极限压测
-
当传统关系型数据库还在为向量相似度搜索的性能挣扎时,Redis7的RedisStack...
- Nginx/OpenResty详解,Nginx Lua编程,重定向与内部子请求
-
重定向与内部子请求Nginx的rewrite指令不仅可以在Nginx内部的server、location之间进行跳转,还可以进行外部链接的重定向。通过ngx_lua模块的Lua函数除了能实现Nginx...
- 一周热门
-
-
C# 13 和 .NET 9 全知道 :13 使用 ASP.NET Core 构建网站 (1)
-
因果推断Matching方式实现代码 因果推断模型
-
git pull命令使用实例 git pull--rebase
-
git pull 和git fetch 命令分别有什么作用?二者有什么区别?
-
面试官:git pull是哪两个指令的组合?
-
git 执行pull错误如何撤销 git pull fail
-
git fetch 和git pull 的异同 git中fetch和pull的区别
-
git pull 之后本地代码被覆盖 解决方案
-
还可以这样玩?Git基本原理及各种骚操作,涨知识了
-
git命令之pull git.pull
-
- 最近发表
- 标签列表
-
- git pull (33)
- git fetch (35)
- mysql insert (35)
- mysql distinct (37)
- concat_ws (36)
- java continue (36)
- jenkins官网 (37)
- mysql 子查询 (37)
- python元组 (33)
- mybatis 分页 (35)
- vba split (37)
- redis watch (34)
- python list sort (37)
- nvarchar2 (34)
- mysql not null (36)
- hmset (35)
- python telnet (35)
- python readlines() 方法 (36)
- munmap (35)
- docker network create (35)
- redis 集合 (37)
- python sftp (37)
- setpriority (34)
- c语言 switch (34)
- git commit (34)