半连接是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. 【java基础】面向对象的三大特征---多态

    java的引用变量有两种类型,编译时类型和运行时类型,如果编译时类型和运行时类型不一致,就会出现多态. 多态分为: 运行时多态 java运行时系统根据调用该方法的实例的类型来决定选择调用哪个方法则被称 ...

  2. 【java基础】成员变量和局部变量02

    成员变量和局部变量(2) 成员变量的初始化和内存中的运行机制         当系统加载类或者创建类的实例的时候,系统会自动为成员变量分配内存空间,并自动指定初始值. package object; ...

  3. Gevent中信号量的使用

    greenlet间同步方法:信号量 1.为什么引入信号量: 2.gevent信号量有哪些: 3.编程实现. 为何引入信号量 信号量是一个允许Greenlet相互合作,限制并发访问或运行的低层次的同步原 ...

  4. java:经典消费生成者

    产品类: public class Info { private String title = "生产"; private String content = "生产罐头& ...

  5. Android N做了啥

    Android N做了哪些改变 一.    性能改善 Doze超级省电模式 手机在关屏同时没有充电的情况,在一段时间后会进入打盹状态,第一阶段会停掉同步.作业.网络等访问,第二阶段会停掉app的位置服 ...

  6. webStorage和cookie的区别

    共同点:         都是保存在浏览器端,且同源的   cookie有什么缺点? Cookie数量和长度的限制.每个domain最多只能有20条cookie,每个cookie长度不能超过4KB 安 ...

  7. Python学习笔记之字典

    一.创建和使用字典 1.创建字典 phonebook={'Alice':'2341','Beth':'9102','Cecil':'3258'} 2.dict,通过映射创建字典 >>> ...

  8. 即时反应的input和propertychange方法

    在web开发中,我们有时会需要动态监听输入框值的变化,当使用onkeydown.onkeypress.onkeyup作为监听事件时,会发现一些复制粘贴等操作用不了,同时,在处理组合快键键的时候也很麻烦 ...

  9. HTML页面优化

    第一步:加载优化 减少HTTP请求. 因为手机浏览器同时响应请求为4个请求(Android支持4个,iOS 5后可支持6个),所以要尽量减少页面的请求数,首次加载同时请求数不能超过4个.a) 合并CS ...

  10. kaggle数据挖掘竞赛初步--Titanic<派生属性&维归约>

    完整代码: https://github.com/cindycindyhi/kaggle-Titanic 特征工程系列: Titanic系列之原始数据分析和数据处理 Titanic系列之数据变换 Ti ...