热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

MYSQLINEXISTSLEFTJOIN结果不同的问题?

随着问问题的同学越来越多,公众号内部私信回答问题已经很困难了,所以建立了一个群,关于各种数据库的问题都可以,目前主要是POS

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: (``.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 using (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



推荐阅读
  • 本文介绍了Paxos的世界中关于复制日志与状态机的概念和重要性。通过存储日志来实现数据的持久化,并通过日志流来记录数据的变化,而不是直接持久化数据本身。这样做的好处是简化了持久化存储的操作,并且方便多机之间的数据同步。 ... [详细]
  • 本文介绍了在开发Android新闻App时,搭建本地服务器的步骤。通过使用XAMPP软件,可以一键式搭建起开发环境,包括Apache、MySQL、PHP、PERL。在本地服务器上新建数据库和表,并设置相应的属性。最后,给出了创建new表的SQL语句。这个教程适合初学者参考。 ... [详细]
  • 本文介绍了数据库的存储结构及其重要性,强调了关系数据库范例中将逻辑存储与物理存储分开的必要性。通过逻辑结构和物理结构的分离,可以实现对物理存储的重新组织和数据库的迁移,而应用程序不会察觉到任何更改。文章还展示了Oracle数据库的逻辑结构和物理结构,并介绍了表空间的概念和作用。 ... [详细]
  • 本文介绍了Java工具类库Hutool,该工具包封装了对文件、流、加密解密、转码、正则、线程、XML等JDK方法的封装,并提供了各种Util工具类。同时,还介绍了Hutool的组件,包括动态代理、布隆过滤、缓存、定时任务等功能。该工具包可以简化Java代码,提高开发效率。 ... [详细]
  • 本文介绍了Redis的基础数据结构string的应用场景,并以面试的形式进行问答讲解,帮助读者更好地理解和应用Redis。同时,描述了一位面试者的心理状态和面试官的行为。 ... [详细]
  • 本文由编程笔记小编整理,介绍了PHP中的MySQL函数库及其常用函数,包括mysql_connect、mysql_error、mysql_select_db、mysql_query、mysql_affected_row、mysql_close等。希望对读者有一定的参考价值。 ... [详细]
  • 解决Cydia数据库错误:could not open file /var/lib/dpkg/status 的方法
    本文介绍了解决iOS系统中Cydia数据库错误的方法。通过使用苹果电脑上的Impactor工具和NewTerm软件,以及ifunbox工具和终端命令,可以解决该问题。具体步骤包括下载所需工具、连接手机到电脑、安装NewTerm、下载ifunbox并注册Dropbox账号、下载并解压lib.zip文件、将lib文件夹拖入Books文件夹中,并将lib文件夹拷贝到/var/目录下。以上方法适用于已经越狱且出现Cydia数据库错误的iPhone手机。 ... [详细]
  • 如何在php中将mysql查询结果赋值给变量
    本文介绍了在php中将mysql查询结果赋值给变量的方法,包括从mysql表中查询count(学号)并赋值给一个变量,以及如何将sql中查询单条结果赋值给php页面的一个变量。同时还讨论了php调用mysql查询结果到变量的方法,并提供了示例代码。 ... [详细]
  • GreenDAO快速入门
    前言之前在自己做项目的时候,用到了GreenDAO数据库,其实对于数据库辅助工具库从OrmLite,到litePal再到GreenDAO,总是在不停的切换,但是没有真正去了解他们的 ... [详细]
  • Centos下安装memcached+memcached教程
    本文介绍了在Centos下安装memcached和使用memcached的教程,详细解释了memcached的工作原理,包括缓存数据和对象、减少数据库读取次数、提高网站速度等。同时,还对memcached的快速和高效率进行了解释,与传统的文件型数据库相比,memcached作为一个内存型数据库,具有更高的读取速度。 ... [详细]
  • 用Vue实现的Demo商品管理效果图及实现代码
    本文介绍了一个使用Vue实现的Demo商品管理的效果图及实现代码。 ... [详细]
  • x86 linux的进程调度,x86体系结构下Linux2.6.26的进程调度和切换
    进程调度相关数据结构task_structtask_struct是进程在内核中对应的数据结构,它标识了进程的状态等各项信息。其中有一项thread_struct结构的 ... [详细]
  • Imtryingtousethisforabasicsearchwithpagination:我正在尝试使用此分区进行基本搜索:$construct?AND? ... [详细]
  • 1print过程procprint<data数据集名><选项>;*label指定打印输出标签noobs制定不显示观测序号*by变量名1< ... [详细]
  • 【原创】利用Python进行河流遥感处理的PyRIS软件开发
    今天开始着手改造pyris1.0.文章地址:https:doi.org10.1016J.ENVSOFT.2018.03.028Monegaglia,2 ... [详细]
author-avatar
黎明前后2502887455
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有