半连接是MySQL 5.6.5引入的,多在子查询exists中使用,对外部row source的每个键值,查找到内部row source匹配的第一个键值后就返回,如果找到就不用再查找内部row source其他的键值了。

测试环境

mysql> desc class;
+------------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+------------+-------------+------+-----+---------+-------+
| class_num | int(11) | NO | PRI | NULL | |
| class_name | varchar(20) | YES | | NULL | |
+------------+-------------+------+-----+---------+-------+
2 rows in set (0.00 sec) mysql> desc roster;
+-------------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------------+---------+------+-----+---------+-------+
| class_num | int(11) | YES | | NULL | |
| student_num | int(11) | YES | | NULL | |
+-------------+---------+------+-----+---------+-------+
2 rows in set (0.00 sec)

roster表中记录的是学生的学号以及对应的教室,多个学生可能在同一个教室,所以字段class_num有重复值

class表中记录的是教室及对应的班级名,字段class_num为唯一值

如果要查询存在学生的班级有哪些

mysql>  SELECT class.class_num, class.class_name FROM class INNER JOIN roster WHERE class.class_num = roster.class_num;
+-----------+------------+
| class_num | class_name |
+-----------+------------+
| 2 | class 2 |
| 3 | class 3 |
| 3 | class 3 |
+-----------+------------+
3 rows in set (0.00 sec)

可以通过distinct去除重复值,但这样做影响性能,所以通过子查询来得出结果

mysql>  SELECT class_num, class_name FROM class WHERE class_num IN (SELECT class_num FROM roster);
+-----------+------------+
| class_num | class_name |
+-----------+------------+
| 2 | class 2 |
| 3 | class 3 |
+-----------+------------+
2 rows in set (0.00 sec)

优化器实际上是将子查询改写为了半连接

mysql> explain SELECT class_num, class_name FROM class WHERE class_num IN (SELECT class_num FROM roster);
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------------------------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------------------------------------------------------------+
| 1 | SIMPLE | roster | NULL | ALL | NULL | NULL | NULL | NULL | 3 | 100.00 | Start temporary |
| 1 | SIMPLE | class | NULL | ALL | PRIMARY | NULL | NULL | NULL | 4 | 25.00 | Using where; End temporary; Using join buffer (Block Nested Loop) |
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------------------------------------------------------------+
2 rows in set, 1 warning (0.00 sec) mysql> show warnings;
+-------+------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Level | Code | Message |
+-------+------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Note | 1003 | /* select#1 */ select `test`.`class`.`class_num` AS `class_num`,`test`.`class`.`class_name` AS `class_name` from `test`.`class` semi join (`test`.`roster`) where (`test`.`class`.`class_num` = `test`.`roster`.`class_num`) |
+-------+------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
Start temporary 和 End temporary的使用表明使用了临时表来去除重复值
如果 select_type 的值为 MATERIALIZED 并且 字段 rows的输出是 <subqueryN> 则表明临时表用于了物化表 select_type value of MATERIALIZED and rows with a table value of <subqueryN>.

如果子查询符合准则(参考文献:http://dev.mysql.com/doc/refman/5.7/en/subquery-optimization.html#semi-joins),MySQL将其转化为semi-join并从以下策略中作出基于cost的选择

  • Convert the subquery to a join, or use table pullout and run the query as an inner join between subquery tables and outer tables. Table pullout pulls a table out from the subquery to the outer query.

  • Duplicate Weedout: Run the semi-join as if it was a join and remove duplicate records using a temporary table.

  • FirstMatch: When scanning the inner tables for row combinations and there are multiple instances of a given value group, choose one rather than returning them all. This "shortcuts" scanning and eliminates production of unnecessary rows.

  • LooseScan: Scan a subquery table using an index that enables a single value to be chosen from each subquery's value group.

  • Materialize the subquery into a temporary table with an index and use the temporary table to perform a join. The index is used to remove duplicates. The index might also be used later for lookups when joining the temporary table with the outer tables; if not, the table is scanned

不确定的内容不敢随意翻译,摘出来原汁原味的文献内容

系统变量optimizer_switch中的semi join 标签控制着半连接是否可用,5.6默认是开启的

MySQL 通过semi join 优化子查询的更多相关文章

  1. MySQL 使用JOIN优化子查询

    1.数据准备 mysql> select * from student; +----+--------+----------+---------+-------------+ | id | na ...

  2. paip.sql索引优化----join 代替子查询法

    paip.sql索引优化----join 代替子查询法 作者Attilax ,  EMAIL:1466519819@qq.com 来源:attilax的专栏 地址:http://blog.csdn.n ...

  3. SQL Server中INNER JOIN与子查询IN的性能测试

    这个月碰到几个人问我关于"SQL SERVER中INNER JOIN 与 IN两种写法的性能孰优孰劣?"这个问题.其实这个概括起来就是SQL Server中INNER JOIN与子 ...

  4. join和子查询的一点点思考

    代码和表设计过程中,为了考虑数据库的范式,通常导致需要join多张表或子查询, 如报表场景, 可此种方式在大数据量的 情况下,效率较低.  如果能做适量的数据冗余,便可以减少join或子查询,效率较高 ...

  5. Mysql-SQL优化-子查询替代LEFT JOIN

    表A:批次信息表, 表B:实际批次明细表, Mysql版本:5.6.36 两表之间的数据体量差异:表B是表A的10000倍. 经过结转,表B通常保留 1千5百万数据.表A就是1千多条数据. 计算近24 ...

  6. MySql优化子查询

    用子查询语句来影响子查询中产生结果rows的数量和顺序. For example: SELECT * FROM t1 WHERE t1.column1 IN (SELECT column1 FROM ...

  7. Mysql基本用法-left join、right join、 inner join、子查询和join-02

    left join #左连接又叫外连接 left join 返回左表中所有记录和右表中连接字段相等的记录  test_user表 phpcvs表 SQL: select * from test_use ...

  8. Database学习 - mysql 数据库 多表/复合/子 查询

    多表查询 多表查询,基本规则,通过两表有关联字段的进行条件匹配查询 内连接查询 方式一: SELECT 查看字段名[,查看字段名] FROM 一表名,二表名 WHERE 一/二表.字段 = 一/二表. ...

  9. 优化子查询sql语句为内连接

    背景: 希望提高查询的效率,从sql语句中频繁出现的子查询入手. 数据表如下:Student表中的CityCode对应于City表中的Code. Student表:                   ...

随机推荐

  1. shell中$0,$?,$!等变量意义

    变量说明:   $$     #Shell本身的PID(ProcessID) $!     #Shell最后运行的后台Process的PID $?     #最后运行的命令的结束代码(返回值) $- ...

  2. JQuery_表单选择器

    表单作为HTML 中一种特殊的元素,操作方法较为多样性和特殊性 开发者不但可以使用之前的常规选择器或过滤器,也可以使用jQuery 为表单专门提供的选择器和过滤器来准确的定位表单元素. 一.常规选择器 ...

  3. arm cache line,PLD指令

    C中嵌入汇编PLD指令:asm("PLD [%0,#128]": :"r" (psrc) ); copy自官方文档: 4.2.7. PLD.PLDW 和 PLI ...

  4. jquery on 绑定多个事件 多个元素

    $('.wrap').on({ click:function(){ //事件1 ...... }, keyup:function() { //事件2 ....... }, keydown:functi ...

  5. Hadoop基础学习框架

    我们主要使用Hadoop的2个部分:分布式文件存储系统(HDFS)和MapReduce计算模型. 关于这2个部分,可以参考一下Google的论文:The Google File System 和 Ma ...

  6. asp.net "true"的小坑

    在cs文件中 写了一个 属性 protected bool IsTrue { get{ return true; } } 在页面 .aspx文件中 在js中 var flag="<%= ...

  7. webform LinQ

    LINQ,语言集成查询(Language Integrated Query)是一组用于c#和VB语言的扩展.它允许编写C#或者Visual Basic代码以查询数据库相同的方式操作内存数据. 他是一个 ...

  8. Spring相关

    一.Spring中ApplicationContext加载机制加载器目前有两种选择:ContextLoaderListener和ContextLoaderServlet. 这两者在功能上完全等同,只是 ...

  9. SoapUI新版本“Ready!API 1.80”体验

    做过接口测试的朋友,肯定都知道一个工具--SoapUI,它强大的功能与集成用例的特性,让不管是开发还是测试,都喜欢用它.在经历了众多版本后,SmartBear公司将SoapUI 从进行了大改版,这也是 ...

  10. Java自由块(静态和非静态)(转载)

    java中的自由块分为两种: 静态块和非静态块 静态块: public class Test { 2 static int x = 10; 3 //静态块:静态块的执行时机是在class文件装载的时候 ...