设为首页 收藏本站
查看: 413|回复: 0

[经验分享] Oracle中left join中右表的限制条件

[复制链接]
YunVN网友  发表于 2016-8-16 07:32:45 |阅读模式
无过滤条件的LEFT JOIN
SQL中最简单形式的LEFT JOIN,是直接根据关联字段,以左表为基准,对右表进行匹配。在SELECT语句中选取的字段,如果有右表的记录(一般都是需要右表的某些记录的),取出配对成功的右表记录中对应的这个字段的值;否则,直接置NULL。这本身就是LEFT JOIN的特点:保证左表记录完整,右表只是辅助匹配。
直接看例子,为了演示,准备了两张测试表test1,test2:
 

select * from test1
DSC0000.png
 
 

select * from test2
DSC0001.png
 
 

select t2.*,t1.market_place_id from test2 t2
left join test1 t1
on t2.parent_asin=t1.asin
DSC0002.png
 对照上面例子解释一下这个结果:以左表test2为基准,用右表test1的asin字段和test2的parent_asin字段进行匹配,取出test2的全部数据和test1的market_place_id字段。对于test2中的第一条记录,因为右表中有两条记录符合的asin='parent1',只是market_place_id不同而已(分别为1、2)。于是这两条记录都会作为符合条件的记录加入结果集。这时,虽然是以左表为基准,但是这条记录却在结果集中产生了两条对应的记录。这点要稍加注意:以左表为基准并不意味着结果集的记录数量=左表的记录数量!

 
再回过头来看结果集的5条记录,由ID字段可以很好的区分出每条记录是由左表的哪条记录对应产生的。这里,最后两条记录可以很好的体现出LEFT JOIN的特点。
 
右表有滤条件的LEFT JOIN
这里,我们忽略左表有过滤条件场景的讨论,因为在LEFT JOIN中左表作为基准表,对他的过滤直接反应在SQL的WHERE字句中,效果上也相当于单表SELECT的WHERE字句过滤,缩小左表范围后,再和右表做JOIN,没什么悬念。
但是对于右表的过滤,通常有两种主要的方式:在ON字句中加入过滤条件或者在LEFT JOIN之后的WHERE字句中加入过滤条件。对于这两种方式的对比,下面主要针对逻辑语义和实现性能上加以对比。
 


  • 过滤条件在ON字句中

 

select t2.*,t1.market_place_id from test2 t2
left join test1 t1
on t2.parent_asin=t1.asin and t1.market_place_id='2'
 上面这条SQL加上了对右表test1中market_place_id的过滤条件:只关心market_place_id为‘2’的右表记录。查询结果如下。   

DSC0003.png
 
逻辑语义上,这个结果相当于右表test1首先进行了条件过滤,只剩下两条记录[(2,'parent1','2'),(3,'parent2','2')],然后左表test2和这个过滤之后的结果集进行无过滤条件的LEFT JOIN,于是得到了上图的结果。
性能上,来看一下这条语句的执行计划截图
DSC0004.png
 可以看出,T1确实先以2为标准对market_place_id做了一次过滤,然后,在外层,再做原来的LEFT JOIN。由此可以证实上面逻辑语义结果的展示,同时也可以发现,就本例而言,如果能够在market_place_id上建立index,可以直接避免内层过滤对右表进行的全表扫描,从而提高整个SQL的执行效率。下图为在market_place_id上建立index之后,同样SQL语句的执行计划:
DSC0005.png
 这里可以看出,原来的TABLE FULL SCAN 已经被换成了INDEX的RANGE SCAN,从而也直接导致了Oracle的优化器在最外层的Hash Join替换为了Nested Loops。(当然这个join的方式并不能说明什么问题,因为毕竟测试用的数据集太小,完全有可能在大数据集的真实情况下,优化器根据统计信息还是最终使用Hash Join算法)




  • 过滤条件在WHERE子句中



select t2.*,t1.market_place_id from test2 t2
left join test1 t1
on t2.parent_asin=t1.asin
where t1.market_place_id='2'
 上面语句的执行结果如下:
DSC0006.png
 

逻辑语义上,所有的market_place_id1!='2'的记录(包括NULL)全部被过滤掉了。

性能上,再来看一下这条语句的执行计划:
DSC0007.png
 

由上面的执行计划可以看出,Oracle也是首先对右表test1进行了market_place_id的过滤,但是过滤之后JOIN操作已经不是LEFT JOIN了,而是变成了普通的INNER JOIN。这就解释了为什么最后的结果集只有两条记录。

同样思路,就本例而言,在右表test的market_place_id字段上建立INDEX,同样可以达到优化SQL的目的,以下是建立INDEX之后的SQL执行计划:
DSC0008.png


结论:

在使用LEFT JOIN时,右表的限制条件,在ON和WHERE字句中出现,逻辑上的语义完全不同

过滤条件在ON子句中出现时,不会改变原来LEFT JOIN的执行语义:以左表为基表。

过滤条件在WHERE字句中出现时,已经改变了原来LEFT JOIN的语义,相当于在最后LEFT JOIN的结果集里面再做了一次WHERE条件的过滤,所以已经丧失的LEFT JOIN的原始语义。

性能上,其实两者并没有本质的区别,扫描路径完全一致,只是对于后者,Oracle的内部实现,巧妙的将上面描述的语义转换为了通过INNER JOIN实现。这样就保证了在真正执行时还是首先进行内层过滤,缩小右表的数据集,然后进行外层INNER JOIN。

所以使用LEFT JOIN是,有需求对右表进行过滤时,要格外小心了。

备注:

以上测试使用Oracle 11g,更老版本的优化器的执行计划可能会不同。但最终语义上不会有差别。

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-258507-1-1.html 上篇帖子: Oracle中TRUNCATE和Delete区别及TRUNCATE使用说明 下篇帖子: Oracle的UTL_FILE.FOPEN的用法(转)
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表