11.1.2 连接过程简介
SELECT * FROM t1, t2 WHERE t1.m1 > 1 AND t1.m1 = t2.m2 AND t2.n2 < 'd'
连接查询的执行过程大致如下:
- 首先确定第一个需要查询的表,称为驱动表
- 从驱动表中每获取到一条记录,都需要到t2表中查找匹配的记录,t2表称为被驱动表
11.1.3 内连接和外连接
针对驱动表中的某条记录,即使在被驱动表中没有匹配的记录,也仍然需要加入到结果集。为了解决这个问题,就有了内连接和外连接的概念
- 对于内连接的两个表,驱动表中的记录在被驱动表中找不到匹配的记录,该记录不会加入到最后的结果集
- 对于外连接的两个表,驱动表中的记录即使在被驱动表中没有匹配的记录,也仍然需要加入到结果集
根据选取驱动表的不同,外连接可以细分为2种:
- 左外连接:选取左侧的表为驱动表
- 右外连接:选取右侧的表为驱动表
WHERE
和ON
的区别
WHERE
子句中的过滤条件
不论是内连接还是外连接,凡是不符合WHERE
子句中的过滤条件的记录都不会被加入最后的结果集。
ON
子句中的过滤条件
对于外连接的驱动表的记录来说,如果无法在被驱动表中找到匹配ON
子句中的过滤条件的记录,那么该记录仍然会被加入到结果集中,对应的被驱动表记录的各个字段使用NULL
值填充。
需要注意的是,这个ON
子句是专门为外连接驱动表中的记录在被驱动表找不到匹配记录时应不应该把该记录加入结果集这个场景下提出的,所以如果把ON
子句放到内连接中,MySQL会把它和WHERE
子句一样对待,也就是说:内连接中的WHERE
子句和ON
子句是等价的
1、左(外)连接的语法
SELECT * FROM t1 LEFT [OUTER] JOIN t2 ON 连接条件 [WHERE 普通过滤条件];
对于LEFT JOIN
类型的连接来说,我们把放在左边的表称之为外表或者驱动表,右边的表称之为内表或者被驱动表
对于左(外)连接和右(外)连接来说,必须使用
ON
子句来指出连接条件
2、右(外)连接的语法
把LEFT
换成RIGHT
而已
SELECT * FROM t1 RIGHT [OUTER] JOIN t2 ON 连接条件 [WHERE 普通过滤条件];
3、内连接的语法
内连接和外连接的根本区别就是在驱动表中的记录不符合ON
子句中的连接条件时不会把该记录加入到最后的结果集
由于在内连接中
ON
子句和WHERE
子句是等价的,所以内连接中不要求强制写明ON
子句
对于内连接来说,驱动表和被驱动表是可以互换的,并不会影响最后的查询结果
11.2 连接的原理
11.2.1 嵌套循环连接
驱动表只访问一次,但被驱动表却可能被多次访问,访问次数取决于对驱动表执行单表查询后的结果集中的记录条数的连接执行方式称之为嵌套循环连接
11.2.2 使用索引加快连接速度
如果访问被驱动表的方式都是全表扫描,就需要扫描很多次,所以可以给连接查询加上索引
SELECT * FROM t1, t2 WHERE t1.m1 > 1 AND t1.m1 = t2.m2 AND t2.n2 < 'd';
这里可以考虑在t2
表的m2
列或者n2
列上加索引
- 在
m2
上加索引,就可以通过“t2.m2=常数值”这样的条件去获取索引记录,如果m2
是主键或者唯一二级索引,代价就是常数级别的。
- 在单表中使用主键值或者唯一二级索引列的值进行等值查找的方式称之为
const
- 连接查询中对被驱动表使用主键值或者唯一二级索引列的值进行等值查找的查询执行方式称之为
eq_ref
- 在
n2
列上建立索引,涉及到的条件是t2.n2 < 'd'
,可能用到range
的访问方法
11.2.3 基于块的嵌套循环连接
Join Buffer
就是执行连接查询前申请的一块固定大小的内存,先把若干条驱动表结果集中的记录装在这个Join Buffer
中,然后开始扫描被驱动表,每一条被驱动表的记录一次性和Join Buffer
中的多条驱动表记录做匹配,因为匹配的过程都是在内存中完成的,所以这样可以显著减少被驱动表的I/O代价
最好的情况是Join Buffer
足够大,能容纳驱动表结果集中的所有记录,这样只需要访问一次被驱动表就可以完成连接操作了
这种加入了Join Buffer
的嵌套循环连接算法称之为基于块的嵌套连接
对于优化被驱动表的查询来说,最好是为被驱动表加上效率高的索引,如果实在不能使用索引,并且自己的机器的内存也比较大可以尝试调大join_buffer_size
的值来对连接查询进行优化