在sql语句多表连接中,in、exists、join哪个效率更高一点

在sql语句多表连接中,in、exists、join哪个效率更高一点,第1张

EXISTS、IN与JOIN,都可以用来实现形如“查询A表中在(或不在)B表中的记录”的查询逻辑。\x0d\\x0d\在查询的两个表大小相当的情况下,3种查询方式的执行时间通常是:\x0d\EXISTS 回答于 2022-11-16

多表联合查询语句:SELECT FROM table LEFT JOIN ON WHERE ORDER BY LIMIT

拿laizijiding的例子说明问题:三个表 板块表(block)、帖子表(post)、会员表(user)

如果查询 帖子列表中 帖子 是属于哪个板块和添加帖子的会员信息,sql语句如下:

SELECT FROM post LEFT JOIN block ONLEFT JOIN user ONWHERE ORDER BY LIMIT (1)

这样写是没有问题的,也可以这样写:

SELECT FROM post LEFT JOIN user ONLEFT JOIN block ONWHERE ORDER BY LIMIT (2)

上面两个到底哪个效率高呢,应该是(1)效率较高,关于连接条件的优化在顺序上有个原则:数剧量少的条件尽量写在前面。一个论坛当中板块的数量要比用户的数量小的多了。

select t_cpid, t_cpproc, t_odid_cpidnum 

from t_odid_cpid 

left join t_cp on t_cpid = t_odid_cpidcpid 

left join T_ORDER on t_odid_cpidodid = T_ORDERodid 

Where T_ORDERB_zzdm = '785390650'。

SQL

结构化查询语言(英文简称:SQL)是一种特殊目的的编程语言,是一种数据库查询和程序设计语言,用于存取数据以及查询、更新和管理关系数据库系统;同时也是数据库脚本文件的扩展名。

结构化查询语言是高级的非过程化编程语言,允许用户在高层数据结构上工作。它不要求用户指定对数据的存放方法,也不需要用户了解具体的数据存放方式,所以具有完全不同底层结构的不同 数据库系统,,可以使用相同的结构化查询语言作为数据输入与管理的接口。结构化查询语言语句可以嵌套,这使它具有极大的灵活性和强大的功能。

一: 数据查询语言( DQL:Data Query Language):

其语句,也称为“数据检索 语句”,用以从表中获得数据,确定数据怎样在应用程序给出。保留字 SELECT是DQL(也是所有SQL)用得最多的动词,其他DQL常用的保留字有WHERE,ORDER BY,GROUP BY和HAVING。这些DQL保留字常与其他类型的SQL语句一起使用。

二: 数据 *** 作语言(DML:Data Manipulation Language):

其语句包括动词 INSERT, UPDATE和 DELETE。它们分别用于添加,修改和删除表中的行。也称为动作查询语言。

三:事务处理语言(TPL):

它的语句能确保被DML语句影响的表的所有行及时得以更新。TPL语句包括BEGIN TRANSACTION,COMMIT和ROLLBACK。

四: 数据控制语言(DCL):

它的语句通过GRANT或REVOKE获得许可,确定单个用户和用户组对 数据库对象的访问。某些RDBMS可用GRANT或REVOKE控制对 表单个列的访问。

五:数据定义语言( DDL):

其语句包括动词CREATE和DROP。在数据库中创建新表或删除表(CREAT TABLE 或 DROP TABLE);为表加入索引等。DDL包括许多与人 数据库目录中获得数据有关的保留字。它也是动作查询的一部分。

六:指针控制语言(CCL):

它的语句,像DECLARE CURSOR,FETCH INTO和UPDATE WHERE CURRENT用于对一个或多个表单独行的 *** 作。

多变关联的实现方式有hash join,merge join,nested loop join 方式,具体使用那种内型的连接,主要依据:

1当前的优化器模式(all_rows和rule)

2取决于表的大小

3取决于关联字段是否有索性

4取决于关联字段是否排序

Hash  join散列连接,优化器选择较小的表(数据量少的表)利用连接键(join key)在内存中建立散列表,将数据存储到hash列表中,然后扫描较大的表

select A,B from A left join B on aid=bid。

先是从A表读取一条记录,用on条件匹配B表的记录,行成n行(包括重复行)如果B表没有与匹配的数据,则select中B表的字段显示为空,接着读取A表的下一条记录,right join类似。

left join基本是A表全部扫描,在表关键中不建议使用子查询作为副表,比如select A,Bfrom A left join (select from b where btype=1 )这样A表是全表扫描,B表也是全表扫描。若果查询慢,可以考虑关联的字段都建索引,将不必要的排序去掉,排序会导致运行慢很多。

主副表条件过滤:

table a(id, type):

id    type

----------------------------------

1      1       

2      1         

3      2   

表b结构和数据

table b(id, class):

id    class

---------------------------------

1      1

2      2

Sql语句1: select a, b from a left join b on aid = bid and atype = 1;

执行结果为:

aid    atype    bid    bclass

----------------------------------------

1        1            1        1

2        1            2        2

3        2

atype=1没有起作用

sql语句2:

select a, b from a left join b on aid = bid where atype = 1;

执行结果为:

aid    atype    bid    bclass

----------------------------------------

1        1            1        1

2        1            2        2

sql语句3:

select a, b from a left join b on aid = bid and bclass = 1;

执行结果为:

aid    atype    bid    bclass

----------------------------------------

1        1            1        1

2        1           

3        2

bclass=1条件过滤成功。

结论:left join中,左表(主表)的过滤条件在on后不起作用,需要在where中添加。右表(副表)的过滤条件在on后面起作用。

Mysql join原理:

Mysql join采用了Nested Loop join的算法,

###坐车 回去补充。

查询指定的记录最好通过Id进行in查询来获得真实的数据其实不是最好而是必须,也就是你应该先查询出复合的ID列表,通过in查询来获得数据

我们来做一个测试ipdatas表:

CREATE TABLE `ipdatas` (

`id` INT(11) NOT NULL AUTO_INCREMENT,

`uid` INT(8) NOT NULL DEFAULT '0',

`ipaddress` VARCHAR(50) NOT NULL,

`source` VARCHAR(255) DEFAULT NULL,

`track` VARCHAR(255) DEFAULT NULL,

`entrance` VARCHAR(255) DEFAULT NULL,

`createdtime` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00',

`createddate` DATE NOT NULL DEFAULT '0000-00-00',

PRIMARY KEY (`id`),

KEY `uid` (`uid`)

) ENGINE=MYISAM AUTO_INCREMENT=67086110 DEFAULT CHARSET=utf8;

这是我们做的广告联盟的推广ip数据记录表,由于我也不是mysql的DBA所以这里咱们仅仅是测试

因为原来里面有大概7015291条数据

这里我们通过jdbc的batch插入6000万条数据到此表当中“JDBC插入6000W条数据用时:9999297ms”;

大概用了两个多小时,这里面我用的是batch大小大概在1w多每次提交,还有一点是每次提交的数据都很小,而且这里用的myisam数据表,因为我需要知道mysql数据库的大小以及索引数据的大小结果是

ipdatasMYD 399 GB (4,288,979,008 字节)

ipdatasMYI 128 GB (1,377,600,512 字节)

这里面我要说的是如果真的是大数据如果时间需要索引还是最好改成数字字段,索引的大小和查询速度都比时间字段可观。

步入正题:

1全表搜索

返回结构是67015297条数据

SELECT COUNT(id) FROM ipdatas;

SELECT COUNT(uid) FROM ipdatas;

SELECT COUNT() FROM ipdatas;

首先这两个全表数据查询速度很快,mysql中包含数据字典应该保留了数据库中的最大条数

查询索引条件

SELECT COUNT() FROM ipdatas WHERE uid=1; 返回结果时间:2分31秒594

SELECT COUNT(id) FROM ipdatas WHERE uid=1; 返回结果时间:1分29秒609

SELECT COUNT(uid) FROM ipdatas WHERE uid=1; 返回结果时间:2分41秒813

第二次查询都比较快因为mysql中是有缓存区的所以增大缓存区的大小可以解决很多查询的优化,真可谓缓存无处不在啊在程序开发中也是层层都是缓存

查询数据

第一条开始查询

SELECT FROM ipdatas ORDER BY id DESC LIMIT 1,10 ; 31毫秒

SELECT FROM ipdatas LIMIT 1,10 ; 15ms

一般情况下没错,但是最好把"="改成

"in"。

为什麽呢?因为你不能保证

(select

top

1

hfid

from

h

where

htime>’2012-10-29

00:00:00’

group

by

hfid

order

by

count

(hfid)

desc

)的结果只有一个值,举个例子:有两个帖子都回覆了10次,其他帖子都在10次以下,这时候你的这个子查询的结果是两个hfid,也就是那两个回覆10次的(并列第一),而不是一个!

MySQL一直被人诟病没有实现HashJoin,最新发布的8018已经带上了这个功能,令人欣喜。有时候在想,MySQL为什么一直不支持HashJoin呢?我想可能是因为MySQL多用于简单的OLTP场景,并且在互联网应用居多,需求没那么紧急。另一方面可能是因为以前完全靠社区,这种演进速度毕竟有限,Oracle收购MySQL后,MySQL的发版演进速度明显加快了很多。

HashJoin本身算法实现并不复杂,要说复杂,可能是优化器配套选择执行计划时,是否选择HashJoin,选择外表,内表可能更复杂一点。不管怎样现在已经有了HashJoin,优化器在选择Join算法时又多了一个选择。MySQL本着实用主义,相信这个功能增强也回应了一些质疑,有些功能不是没有能力做好,而是有它的优先级。

在8018之前,MySQL只支持NestLoopJoin算法,最简单的就是Simple NestLoop Join,MySQL针对这个算法做了若干优化,实现了Block NestLoop Join,Index NestLoop Join和Batched Key Access等,有了这些优化,在一定程度上能缓解对HashJoin的迫切程度。下文会单独拿一个章节讲MySQL的这些Join优化,下面先讲HashJoin。

Hash Join算法

NestLoopJoin算法简单来说,就是双重循环,遍历外表(驱动表),对于外表的每一行记录,然后遍历内表,然后判断join条件是否符合,进而确定是否将记录吐出给上一个执行节点。从算法角度来说,这是一个MN的复杂度。HashJoin是针对equal-join场景的优化,基本思想是,将外表数据load到内存,并建立hash表,这样只需要遍历一遍内表,就可以完成join *** 作,输出匹配的记录。如果数据能全部load到内存当然好,逻辑也简单,一般称这种join为CHJ(Classic Hash Join),之前MariaDB就已经实现了这种HashJoin算法。如果数据不能全部load到内存,就需要分批load进内存,然后分批join,下面具体介绍这几种join算法的实现。

In-Memory Join(CHJ)

HashJoin一般包括两个过程,创建hash表的build过程和探测hash表的probe过程。

1)build phase

遍历外表,以join条件为key,查询需要的列作为value创建hash表。这里涉及到一个选择外表的依据,主要是评估参与join的两个表(结果集)的大小来判断,谁小就选择谁,这样有限的内存更容易放下hash表。

2)probe phase

hash表build完成后,然后逐行遍历内表,对于内表的每个记录,对join条件计算hash值,并在hash表中查找,如果匹配,则输出,否则跳过。所有内表记录遍历完,则整个过程就结束了。过程参照下图,来源于MySQL官方博客

左侧是build过程,右侧是probe过程,country_id是equal_join条件,countries表是外表,persons表是内表。

On-Disk Hash Join

CHJ的限制条件在于,要求内存能装下整个外表。在MySQL中,Join可以使用的内存通过参数join_buffer_size控制。如果join需要的内存超出了join_buffer_size,那么CHJ将无能为力,只能对外表分成若干段,每个分段逐一进行build过程,然后遍历内表对每个分段再进行一次probe过程。假设外表分成了N片,那么将扫描内表N次。这种方式当然是比较弱的。在MySQL80中,如果join需要内存超过了join_buffer_size,build阶段会首先利用hash算将外表进行分区,并产生临时分片写到磁盘上;然后在probe阶段,对于内表使用同样的hash算法进行分区。由于使用分片hash函数相同,那么key相同(join条件相同)必然在同一个分片编号中。接下来,再对外表和内表中相同分片编号的数据进行CHJ的过程,所有分片的CHJ做完,整个join过程就结束了。这种算法的代价是,对外表和内表分别进行了两次读IO,一次写IO。相对于之之前需要N次扫描内表IO,现在的处理方式更好。

第一张图是外表的分片过程,第二张图是内表的分片过程,第三张图是对分片进行build+probe过程。

Grace Hash Join

主流的数据库Oracle,SQLServer,PostgreSQL早就支持了HashJoin。Join算法都类似,这里介绍下Oracle使用的Grace Hash Join算法。其实整个过程与MySQL的HashJoin类似,主要有一点区别。当出现join_buffer_size不足时,MySQL会对外表进行分片,然后再进行CHJ过程。但是,极端情况下,如果数据分布不均匀,导致大量的数据hash后都分布在一个分桶中,导致分片后,join_buffer_size仍然不够,MySQL的处理方式是一次读分片读若干记录构建hash表,然后probe对应的外表分片。处理完一批后,清理hash表,重复上述过程,直到这个分片的所有数据处理完为止。这个过程与CHJ在join_buffer_size不足时,处理逻辑相同。

GraceHash在遇到这种情况时,会继续分片进行二次Hash,直到内存足够放下一个hash表为止。但是,这里仍然有极端情况,如果输入join条件都相同,那么无论进行多少次Hash,都没法分开,那么这个时候GraceHashJoin也退化成和MySQL的处理方式一样。

hybrid hash join

与GraceHashJoin的区别在于,如果缓存能缓存足够多的分片数据,会尽量缓存,那么就不必像GraceHash那样,严格地将所有分片都先读进内存,然后写到外存,然后再读进内存去走build过程。这个是在内存相对于分片比较充裕的情况下的一种优化,目的是为了减少磁盘的读写IO。目前Oceanbase的HashJoin采用的是这种join方式。

MySQL-Join算法优化

在MySQL8018之前,也就是在很长一段时间内,MySQL数据库并没有HashJoin,主要的Join算法是NestLoopJoin。SimpleNestLoopJoin显然是很低效的,对内表需要进行N次全表扫描,实际复杂度是NM,N是外表的记录数目,M是记录数,代表一次扫描内表的代价。为此,MySQL针对SimpleNestLoopJoin做了若干优化,下面贴的均来自网络。

BlockNestLoopJoin(BNLJ)

MySQL采用了批量技术,即一次利用join_buffer_size缓存足够多的记录,每次遍历内表时,每条内表记录与这一批数据进行条件判断,这样就减少了扫描内表的次数,如果内表比较大,间接就缓解了IO的读压力。

IndexNestLoopJoin(INLJ)

如果我们能对内表的join条件建立索引,那么对于外表的每条记录,无需再进行全表扫描内表,只需要一次Btree-Lookup即可,整体时间复杂度降低为NO(logM)。对比HashJoin,对于外表每条记录,HashJoin是一次HashTable的search,当然HashTable也有build时间,还需要处理内存不足的情况,不一定比INLJ好。

Batched Key Access

IndexNestLoopJoin利用join条件的索引,通过Btree-Lookup去匹配减少了遍历内表的代价。如果join条件是非主键列,那么意味着大量的回表和随机IO。BKA优化的做法是,将满足条件的一批数据按主键排序,这样回表时,从主键的角度来说就相对有序,缓解随机IO的代价。BKA实际上是利用了MRR特性(MultiRangeRead),访问数据之前,先将主键排序,然后再访问。主键排序的缓存大小通过参数read_rnd_buffer_size控制。

总结

MySQL80以后,Server层代码做了大量的重构,虽然优化器相对于Oracle还有很大差距,但一直在进步。HashJoin的支持使得MySQL优化器有更多选择,SQL的执行路径也能做到更优,尤其是对于等值join的场景。虽然MySQL之前对于Join做过若干优化,比如NBLJ,INLJ以及BKA等,但这些代替不了HashJoin的作用。一个好用的数据库就应该具备丰富的基础能力,利用优化器分析出合适场景,然后拿出对应的基础能力以最高效的方式响应请求。

以上就是关于在sql语句多表连接中,in、exists、join哪个效率更高一点全部的内容,包括:在sql语句多表连接中,in、exists、join哪个效率更高一点、多个表left join 用 order by fetch first rows only 放在join之前优化,怎么写、SQL三表联合查询的语句如何优化等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!

欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/sjk/9861538.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2023-05-02
下一篇2023-05-02

发表评论

登录后才能评论

评论列表(0条)

    保存