MySql系列(三):JOIN的原理和算法

JOIN

博主今天为大家带来JOIN的原理,只有少部分,有待补充提高,如有更好地建议,欢饮讨论!

以下所写内容均与以前的文章有联系可以前往博文查看,陈永佳的博客

什么是JOIN?

JOIN的含义就如英文单词“join”一样,连接两张表,大致分为内连接,外连接,右连接,左连接,自然连接。这里描述先甩出一张用烂了的图,然后插入测试数据。

用烂了的图

笛卡尔积:CROSS JOIN

要理解各种JOIN首先要理解笛卡尔积。笛卡尔积就是将A表的每一条记录与B表的每一条记录强行拼在一起。所以,如果A表有n条记录,B表有m条记录,笛卡尔积产生的结果就会产生n*m条记录。

建表:(想查询先建表)
CREATE DATABASE db0206;
USE db0206;

CREATE TABLE `db0206`.`tbl_dept`(  
  `id` INT(11) NOT NULL AUTO_INCREMENT,
  `deptName` VARCHAR(30),
  `locAdd` VARCHAR(40),
  PRIMARY KEY (`id`)
) ENGINE=INNODB CHARSET=utf8mb4;

CREATE TABLE `db0206`.`tbl_emp`(  
  `id` INT(11) NOT NULL AUTO_INCREMENT,
  `name` VARCHAR(20),
  `deptId` INT(11),
  PRIMARY KEY (`id`),
  FOREIGN KEY (`deptId`) REFERENCES `db0206`.`tb_dept`(`id`)
) ENGINE=INNODB CHARSET=utf8mb4;
/*插入数据*/
INSERT INTO tbl_dept(deptName,locAdd) VALUES('CC',21);
INSERT INTO tbl_dept(deptName,locAdd) VALUES('MM',21);
INSERT INTO tbl_dept(deptName,locAdd) VALUES('SC',22);
INSERT INTO tbl_dept(deptName,locAdd) VALUES('SC',23);
INSERT INTO tbl_dept(deptName,locAdd) VALUES('EH',65);

INSERT INTO tbl_emp(NAME,deptId) VALUES('c1',1);
INSERT INTO tbl_emp(NAME,deptId) VALUES('c2',1);
INSERT INTO tbl_emp(NAME,deptId) VALUES('c3',1);

INSERT INTO tbl_emp(NAME,deptId) VALUES('m5',2);
INSERT INTO tbl_emp(NAME,deptId) VALUES('m6',2);

INSERT INTO tbl_emp(NAME,deptId) VALUES('s7',3);

INSERT INTO tbl_emp(NAME,deptId) VALUES('s8',4);

下列查询与该表数据有异请自行忽略


内连接

内连接文氏图

  • 执行的sql语句以及执行的查询结果:两个内连表的共有部分
    在这里插入图片描述
左外连接

在这里插入图片描述

  • 执行的sql语句以及执行的查询结果:左表的独有部分, 右表的为空部分置为空
    在这里插入图片描述
右外连接

在这里插入图片描述

  • 执行的sql语句以及执行的查询结果:
    在这里插入图片描述
左连接

在这里插入图片描述

  • 执行的sql语句以及执行的查询结果:
    在这里插入图片描述
右连接

在这里插入图片描述

  • 执行的sql语句以及执行的查询结果:
    在这里插入图片描述
全连接

在这里插入图片描述

  • 执行的sql语句以及执行的查询结果
    在这里插入图片描述
两张表中都没有出现的数据集

在这里插入图片描述

  • 执行的sql语句以及执行的查询结果
    在这里插入图片描述

表连接算法

Nested Loop Join(NLJ)算法

首先介绍一种基础算法:NLJ,嵌套循环算法。循环外层是驱动表,循坏内层是被驱动表。驱动表会驱动被驱动表进行连接操作。首先驱动表找到第一条记录,然后从头扫描被驱动表,逐一查找与驱动表第一条记录匹配的记录然后连接起来形成结果表中的一条记。被驱动表查找完后,再从驱动表中取出第二个记录,然后从头扫描被驱动表,逐一查找与驱动表第二条记录匹配的记录,连接起来形成结果表中的一条记录。重复上述操作,直到驱动表的全部记录都处理完毕为止。这就是嵌套循环连接算法的基本思想,伪代码如下。

foreach row1 from t1
    foreach row2 from t2
        if row2 match row1 //row2与row1匹配,满足连接条件
            join row1 and row2 into result //连接row1和row2加入结果集

首先加载t1,然后从t1中取出第一条记录,之后加载t2表,与t2表中的记录逐个匹配,连接匹配的记录。

Block Nested Loop Join(BNLJ)算法

再介绍一种高级算法:BNLJ,块嵌套循环算法,可以看作对NLJ的优化。大致思想就是建立一个缓存区,一次从驱动表中取多条记录,然后扫描被驱动表,被驱动表的每一条记录都尝试与缓冲区中的多条记录匹配,如果匹配则连接并加入结果集。缓冲区越大,驱动表一次取出的记录就越多。这个算法的优化思路就是减少内循环的次数从而提高表连接效率。(博主用的很少哈哈!)

影响性能的因素

1.内循环的次数:现在考虑这么一个场景,当t1有100条记录,t2有10000条记录。那么,t1驱动t2与t2驱动t1,他们之间在效率上孰优孰劣?如果是单纯的分析指令执行次数,他们都是100*10000,但是考虑到加载表的次数呢。首先分析t1驱动t2,t1表加载1次,t2表需要加载100次。然后分析t2驱动t1,t2表首先加载1次,但是t1表要加载10000次。所以,t1驱动t2的效率要优于t2驱动t1的效率。小表驱动大表能够减少内循环的次数从而提高连接效率。
另外,如果使用Block Nested Loop Join算法的话,通过扩大一次缓存区的大小也能减小内循环的次数。由此又可得,设置合理的缓冲区大小能够提高连接效率

2.快速匹配:扫描被驱动表寻找合适的记录可以看做一个查询操作,如何提高查询的效率呢?建索引啊!由此还可得出,在被驱动表建立索引能够提高连接效率

3.排序:假设t1表驱动t2表进行连接操作,连接条件是t1.id=t2.id,而且要求查询结果对id排序。现在有两种选择,方式一[…ORDER BY t1.id],方式二[…ORDER BY t2.id]。如果我们使用方式一的话,可以先对t1进行排序然后执行表连接算法,如果我们使用方式二的话,只能在执行表连接算法后,对结果集进行排序(Using temporary),效率自然低下。由此最后可得出,优先选择驱动表的属性进行排序能够提高连接效率。

引用

《数据库系统概论》:https://my.oschina.net/xinxingegeya/blog/495897

如何使用

5.6版本及以后,优化器管理参数optimizer_switch中中的block_nested_loop参数控制着BNL是否被用于优化器。默认条件下是开启,若果设置为off,优化器在选择 join方式的时候会选择NLJ算法。

最后

喜欢博主的小伙伴可以加个关注、点个赞哦,持续更新嘿嘿!

  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

陈永佳

你的鼓励将是我创作的最大动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值