MYSQL IN EXISTS LEFT JOIN 结果不同的问题?

news2024/11/16 19:42:03

3eeba640dbb70118e9cb406ac804d3af.png

随着问问题的同学越来越多,公众号内部私信回答问题已经很困难了,所以建立了一个群,关于各种数据库的问题都可以,目前主要是 POSTGRESQL, MYSQL ,MONGODB ,POLARDB ,REDIS 等,期待你的加入,加群请添加微信liuaustin3.

这个问题的从下面的这个SQL 来开始,这是一个典型的说复杂不复杂,说写的好,写的不怎么好的一个SQL。

select fi.title,fi.film_id  from film as fi

left join film_category as fc on fi.film_id = fc.film_id

and exists (select 1 from film_actor as fa where fa.film_id = fi.film_id and fa.film_id = 200);

执行计划

——————————————————————————————

| -> Nested loop left join  (cost=453.00 rows=1000) (actual time=0.045..1.954 rows=1000 loops=1)

    -> Covering index scan on fi using idx_title  (cost=103.00 rows=1000) (actual time=0.028..0.236 rows=1000 loops=1)

    -> Nested loop inner join  (cost=100.35 rows=1) (actual time=0.002..0.002 rows=0 loops=1000)

        -> Filter: (fi.film_id = 200)  (cost=0.25 rows=1) (actual time=0.001..0.001 rows=0 loops=1000)

            -> Covering index lookup on fc using PRIMARY (film_id=200)  (cost=0.25 rows=1) (actual time=0.001..0.001 rows=1 loops=1000)

        -> Filter: (`<subquery2>`.film_id = 200)  (cost=18.27..18.27 rows=1) (actual time=0.018..0.018 rows=1 loops=1)

            -> Single-row index lookup on <subquery2> using <auto_distinct_key> (film_id=fi.film_id)  (actual time=0.001..0.001 rows=1 loops=1)

                -> Materialize with deduplication  (cost=1.35..1.35 rows=5) (actual time=0.018..0.018 rows=1 loops=1)

                    -> Filter: (fa.film_id is not null)  (cost=0.80 rows=5) (actual time=0.008..0.010 rows=3 loops=1)

                        -> Covering index lookup on fa using idx_fk_film_id (film_id=200)  (cost=0.80 rows=5) (actual time=0.007..0.009 rows=3 loops=1)

这里执行计划出现了 Materialize with deduplication ,这个到底是一个什么意思,今天的问题就从这里开始了。

这里Materialize with deduplication 的意思是,当第一次MYSQL需要这个子查询的结果的情况下,会将临时结果产生为一个临时表,当再次需要这个结果的时候会再次调用。通过给临时表用散列表对表进行索引,索引为唯一索引去除重复值。

这样的好处有两个

1  可以尽量不进行语句的改写

2  可以重复的调用

3063da9069575067f5fbb0f71fc20ace.png

这个功能本身 materialization=on 设置为ON 才能在查询中使用这个功能

mysql> SELECT @@optimizer_switch\G

*************************** 1. row ***************************

@@optimizer_switch: index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,engine_condition_pushdown=on,index_condition_pushdown=on,mrr=on,mrr_cost_based=on,block_nested_loop=on,batched_key_access=off,materialization=on,semijoin=on,loosescan=on,firstmatch=on,duplicateweedout=on,subquery_materialization_cost_based=on,use_index_extensions=on,condition_fanout_filter=on,derived_merge=on,use_invisible_indexes=off,skip_scan=on,hash_join=on,subquery_to_derived=off,prefer_ordering_index=on,hypergraph_optimizer=off,derived_condition_pushdown=on

下面我们分析一下,什么时候会出现 materialize with deduplication 

df9a2310a80e3cb426912e264ae27280.png

e553bc875f9e0603d84fbb5112a614a7.png

语句1   select film_id,title,last_update from film as fi where fi.film_id  in (select fa.film_id from film_actor as fa where fa.film_id = 200);

6b4b19043772228dfff187b420ea6ab8.png

第一个语句并没有出现materialize with deduplication,而是通过索引连接的方式将子查询进行了处理,原因是因为两个表的主键都包含了 film_id 并且子查询中的条件也是主键包含,所以语句优化的过程中并没有出现 materialize with deduplication.

那么我们将条件更换,将子查询中的条件更换为lat_update

select film_id,title,last_update from film as fi where fi.film_id in (select fa.film_id from film_actor as fa where fa.last_update > '2006-12-12');

81672858e0aee9c71af92165a38ce0bc.png

在语句变化后,条件变为了非主键的条件后,就产生了 Materialize with deduplication 同时产生了子查询的结果后,并且结果为一行,将主表和产生的新的临时表进行了 nested loop inner join的操作。

此时我们优化这个查询,因为cost 太高了,我们针对这个查询添加了film_actor 中的字段 last_update的索引。然后在次查看执行计划后,发现整体的cost 大幅度降低。

675649b702deaf449b72c0411e1146e8.png

通过这个问题,可以看出虽然有 masterialize with deduplication 但是对于子查询中的数据的过滤还是一个必选项,也可以有效的提高查询的效率。

上面查询中使用了IN 和 EXISTS ,如果我们通过 not in  和  not exists 来看看执行计划是否有变化。通过下面两个语句,可以看到整体的执行计划的改变

explain format=tree select film_id,title,last_update from film as fi where not exists ( select 1 from film_actor as fa where fa.film_id = fi.film_id and fa.last_update > '2005-12-12');

explain format=tree select film_id,title,last_update from film as fi where fi.film_id not in (select fa.film_id from film_actor as fa where fa.last_update > '2005-12-12');

整体的执行计划变更中,虽然使用的 last_update 的索引,但并没有提高查询效率,同时因为是排除在查询中还添加  film_id is not null , 然后使用了MYSQL 8.021 后提供的 antijoin 的方式来进行两个表的反向的数据连接。

e8fa943014d88b06bdbdd2144681db6c.png

但是整体的数据查询的效率 cost 很高,这也从侧面说明语句在撰写中,尽量还是要避免 NOT  IN  , NOT EXISTS 。

explain format=tree select count(fi.film_id)

from film as fi

left join film_category as fc on fi.film_id = fc.film_id

and fi.film_id in  (select film_id from film_actor as fa where fa.film_id = fi.film_id and fa.film_id = 2);

explain format=tree select count(fi.film_id)

from film as fi

left join film_category as fc on fi.film_id = fc.film_id

and exists (select * from film_actor as fa where fa.film_id = fi.film_id and fa.film_id = 2);

explain analyze select count(fi.film_id)  

 from film as fi  

 left join film_category as fc on fi.film_id = fc.film_id   

 left join film_actor as fa on fa.film_id = fi.film_id and fa.film_id = 2;

上面的三个SQL 看上去要表达一个目的,实际上从结果上看,1 2 SQL 的结果是一致的,第三个用 LEFT JOIN 表达的SQL 的结果和前两个不一样。

486bcd563e6eff7472f5fb374372d1cf.png

4f9572bdb5198396384ba04dde028041.png

0b6da01359af332877fff887fbb8acc0.png

这里结果的不同主要有几个问题

1  IN EXIST 在数据结果查询中,是有去重的功能的。

7d2ba1df31285a48f5ea574ee4525e57.png

2  LEFT JOIN 是是存在一对多的关系

ae789341481b7951857646e6920a8fed.png

见下图这个就是,通过left JOIN 查询后的数据,明显与上个 EXIST ,IN 的结果中,多个 3个 2 原因是在于

4df83935e1388ab0caf7dd224a9d85ee.png

实际上在film_actor 中就存在 4条 film_id =2 的记录,所以LEFT JOIN 如实的包括了4 个2 的记录, 而 EXIST  IN 则带有去重的功能,所以在结果中只有一个 2 的记录。

31a12cf07599eb383e99e636a8f2efbb.png

如果要LEFT JOIN 中查询的结果与 EXIST IN 一致则可以在查询语句中加入group by 来去重。

select count(*) from (

select count(fi.film_id)

 from film as fi  

 left join film_category as fc on fi.film_id = fc.film_id   

 left join film_actor as fa on fa.film_id = fi.film_id and fa.film_id = 2

 group by fi.film_id) as t;

3f3ed7100f41278203bb0bfa1ff5fcad.png

所以在撰写语句的时候,要明白 IN  EXIST 和 LEFT JOIN 之间的区别,避免结果不是自己要的。

3ea9cabb91621523be1083e14acc8bc4.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/125906.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

智慧园区建设面临挑战,该如何应对?

随着全球物联网、移动互联网、云计算等新一轮信息技术的迅速发展和深入应用&#xff0c;“智慧园区”建设已成为发展趋势。近年来&#xff0c;我国的产业园区也向着智慧化、创新化、科技化转变。中国经济正在进入转型升级的关键时期&#xff0c;各地产业竞争态势越发激烈。可以…

ClickHouse 大数据量的迁移方式

关于Clickhouse 备份方式&#xff0c;其官方网站上就提供了多种备份方式可以参考&#xff0c;不同的业务需求有不同的使用场景&#xff0c;需要使用不同的备份方式&#xff0c;不存在一个通用的解决方案可以应对各种情况下的ClickHouse备份和恢复。今天这个文字&#xff0c;我们…

Qt+第三方库开发遇到的坑---kalrry

Qt依赖UG库开发遇到的坑---kalrry一、依赖引入坑二、Qt在Debug时报错1、编译器是 **MSVC** 还是 **MinGW**2、编译器 32位 还是 64位三、QtMSVC编译后中文乱码四、不能将const char*类型的值分配到const* 类型的实体五、debug编译后再发布程序无法运行六、Qt 环境配置提示警告警…

Spring @Transactional注解事务六大失效场景

Transactional事务失效场景1&#xff1a;注解在非public修饰的方法上。 原因&#xff1a;Spring强制的要求。 代码示例&#xff1a; Transactionalprivate void createOrder(){} Transactional事务失效场景2&#xff1a;注解在被final关键字修饰的方法上。 原因&#xff1a;Spr…

推荐一款好用的设备维护管理系统,你用过了吗

设备维护管理系统层出不穷&#xff0c;找到一款好用的适配的&#xff0c;也要花费大量的时间去挑选&#xff01; 对于企业来说&#xff0c;一个好的设备管理系统应该能够&#xff1a; 1. 适应企业高度场景化的设备管理工作&#xff0c;覆盖设备的采购、常规检查、养护、添装、…

“三刷”牛客网844道Java题目,易错知识点总结,带你清扫Java基础面试障碍

目录 前言 1、子类通过哪些办法&#xff0c;可以调用继承自父类的方法&#xff1f; 2、volatile、Lock、transient 哪个关键字不能用来处理线程安全 3、Hashtable 和 HashMap 的区别是&#xff1f;&#xff08;容易忽略的两点&#xff09; 4、如何声明了一个适合于创建50个字…

flask框架实现文件下载接口

方式一&#xff1a; app.route("/download1") def download():# return send_file(test.exe, as_attachmentTrue)return send_file(2.jpg)# return send_file(1.mp3)如果不加as_attachment参数&#xff0c;则会向浏览器发送文件&#xff0c;比如发送一张图片&#x…

Revit技巧:快速隔离一个小构件,拉伸屋顶转折处连接

一、Revit中如何快速单独隔离一个小构件 今天跟大家分享的是一个快速隔离的小技巧&#xff0c;你可以理解为快速用剖面框拉成你需要的构件区域。我就举个例子让大家简单容易理解&#xff0c;如图1所示&#xff0c;假设我只需要编辑那个墙的装饰&#xff0c;但又需要用剖面框&am…

704二分查找法--搜索区间

二分查找法–搜索区间的深入理解 二分查找法是算法学习中很基础的算法&#xff0c;但是其也是很重要的算法&#xff0c;将二分查找法搞明白对后续算法的学习有着事半功倍的作用。 本体难点&#xff1a;二分搜索区间的判断 搜索区间 [left,right] int search(vector<int>…

026_SS_MoFusion A Framework for Denoising-Diffusion-based Motion Synthesis

MoFusion: A Framework for Denoising-Diffusion-based Motion Synthesis 本文提出了一种利用diffusion生成人体motion的方法。这种方法可以将音频和文本作为条件。 损失函数 对于diffusion的损失中&#xff0c;加入了运动损失。 其中第一项 LdaL_{da}Lda​ 是原始的diffusio…

微信公众号如何接入ChatGPT机器人

不难&#xff0c;代码总共也就25行&#xff0c;大致逻辑如下。 总共分为是下面两步 文章目录在云服务器上部署自定义消息处理服务微信公众号配置自己的消息处理服务器在云服务器上部署自定义消息处理服务 这里需要我们自定义来处理用户发送过来的消息 首先导入werobot&#x…

Linux操作系统实验2——进程描述

实验要求&#xff1a; 1.查看task_struct的结构&#xff0c;找到其中的pid&#xff0c;state&#xff0c;prio&#xff0c;parent pid字段 2.在task_struct结构中找到vma相关字段&#xff0c;vm_start,vm_end,vm_next 3.打印指定pid的基本信息&#xff0c;包括基本信息及VMA内存…

分组卷积与dw卷积

分组卷积&#xff08;Group Convolution&#xff09; 分组卷积在ResNext中用到了 首先必须明确&#xff1a; 常规卷积&#xff08;Convolution&#xff09;的参数量是&#xff1a; K*K*C_in*n K是卷积核大小&#xff0c;C_in是input的channel数&#xff0c;n是卷积核个数(outp…

共享模型之管程(一)

1.共享带来的问题 1.1.线程安全问题 例如: 两个线程对初始值为0的静态变量一个做自增,一个做自减,各做5000次,结果是0吗? Slf4j public class TestThread {//静态共享变量static int counter 0;public static void main(String[] args) throws InterruptedException {Threa…

【Axure教程】拖动排序——扣款顺序

随着移动支付的发展&#xff0c;移动支付的途径和方式也越来越多&#xff0c;常见的有钱包余额支付、支付宝支付、微信支付、银行卡支付……随着绑定的账户越来越多&#xff0c;我们需要一个设置扣款顺序的功能页面。 所以今天作者就教大家如果做一个拖动排序的扣款顺序的原型…

机器学习的4种经典模型总结

机器学习&#xff08;Machine Learning&#xff09;是人工智能的一个分支&#xff0c;也是人工智能的一种实现方法。机器学习的核心是“使用算法解析数据&#xff0c;从中学习&#xff0c;然后对新数据做出决定或预测”&#xff0c;机器学习的概念就是通过输入海量训练数据对模…

【财务】FMS财务管理系统---质保金与预付款

在FMS财务管理系统中&#xff0c;如何对质保金和预付款进行管理&#xff0c;笔者做了详细的业务流程拆解。 上一篇主要说了财务应收管理&#xff0c;有一些朋友留言提出了很多建议&#xff0c;在这里必须谢谢。 关于应收分为ToC与ToB两部分&#xff0c;每一部分都与前端业务系…

新一代自动出价范式:在线强化学习SORL框架

丨目录&#xff1a; 摘要 动机&#xff1a;在离线不一致问题 问题建模 方法&#xff1a;SORL框架 实验结果 总结 关于我们 参考文献▐ 摘要近年来&#xff0c;自动出价已成为广告主提升投放效果的重要方式&#xff0c;在真实广告系统&#xff08;RAS&#xff09;中&#xff0c;…

C++ 数学与算法系列之高斯消元法求解线性方程组

1. 前言 什么是消元法&#xff1f; 消元法是指将多个方程式组成的方程组中的若干个变量通过有限次地变换&#xff0c;消去方程式中的变量&#xff0c;通过简化方程式&#xff0c;从而获取结果的一种解题方法。 消元法主要有代入消元法、加减消元法、整体消元法、换元消元法、…

【C/C++ SOCKET编程】实现服务器客户端的简单通信

什么是SOCKET Socket又称"套接字"&#xff0c;应用程序通常通过"套接字"向网络发出请求或者应答网络请求&#xff0c;使主机间或者一台计算机上的进程间可以通讯。 TCP/IP协议 从字面意义上讲&#xff0c;有人可能会认为 TCP/IP 是指 TCP 和 IP 两种协议…