MySQL子查询原理的深入分析

目录
  • 01前言
  • 02准备内容
  • 03子查询的语法形式和分类
    • 3.1 语法形式
      • 3.1.1  FROM子句中
      • 3.1.2 WHERE或IN子句中
    • 3.2 分类
      • 3.2.1 按返回的结果集区分
      • 3.2.2 按与外层查询关系来区分
  • 04子查询在MySQL中是怎么执行的
    • 4.1 标量子查询、行子查询的执行方式
      • 4.1.1 不相关子查询
      • 4.1.2 相关的子查询
    • 4.2 IN子查询
      • 4.2.1 物化
      • 4.2.2 半联接的实现:
      • 4.2.3 半联接的适用条件
      • 4.2.4 转为 EXISTS 子查询
  • 05总结

01前言

子查询,通俗解释就是查询语句中嵌套着另一个查询语句。相信日常工作中接触到 MySQL 的同学都了解或使用过子查询,但是具体它是怎样实现的呢? 查询效率如何? 这些恐怕好多人就不太清楚了,下面咱们就围绕这两个问题共同探索一下。

02准备内容

这里我们需要用到3个表,这3个表都有一个主键索引 id 和一个索引 a,字段 b 上无索引。存储过程 idata() 往表 t1 里插入的是 100 行数据,表 t2、t3 里插入了 1000 行数据。建表语句如下:

CREATE TABLE `t1` (
    `id` INT ( 11 ) NOT NULL,
    `t1_a` INT ( 11 ) DEFAULT NULL,
    `t1_b` INT ( 11 ) DEFAULT NULL,
PRIMARY KEY ( `id` ),
KEY `idx_a` ( `t1_a` )) ENGINE = INNODB;

CREATE TABLE `t2` (
    `id` INT ( 11 ) NOT NULL,
    `t2_a` INT ( 11 ) DEFAULT NULL,
    `t2_b` INT ( 11 ) DEFAULT NULL,
PRIMARY KEY ( `id` ),
KEY `idx_a` ( `t2_a` )) ENGINE = INNODB;

CREATE TABLE `t3` (
    `id` INT ( 11 ) NOT NULL,
    `t3_a` INT ( 11 ) DEFAULT NULL,
    `t3_b` INT ( 11 ) DEFAULT NULL,
PRIMARY KEY ( `id` ),
KEY `idx_a` ( `t3_a` )) ENGINE = INNODB;

-- 向t1添加100条数据
-- drop procedure idata;
delimiter ;;
create procedure idata()
begin
  declare i int;
  set i=1;
  while(i<=100)do
        insert into t1 values(i, i, i);
    set i=i+1;
  end while;
end;;
delimiter ;
call idata();

-- 向t2添加1000条数据
drop procedure idata;
delimiter ;;
create procedure idata()
begin
  declare i int;
  set i=101;
  while(i<=1100)do
        insert into t2 values(i, i, i);
    set i=i+1;
  end while;
end;;
delimiter ;
call idata();

-- 向t2添加1000条数据,且t3_a列的值为倒叙
drop procedure idata;
delimiter ;;
create procedure idata()
begin
  declare i int;
  set i=101;
  while(i<=1100)do
        insert into t3 values(i, 1101-i, i);
    set i=i+1;
  end while;
end;;
delimiter ;
call idata();

03子查询的语法形式和分类

3.1 语法形式

子查询的语法规定,子查询可以在一个外层查询的各种位置出现,这里我们只介绍常用的几个:

3.1.1  FROM子句中

SELECT m, n FROM (SELECT m2 + 1 AS m, n2 AS n FROM t2 WHERE m2 > 2) AS t;

这个例子中的子查询是:(SELECT m2 + 1 AS m, n2 AS n FROM t2 WHERE m2 > 2),这个放在FROM子句中的子查询相当于一个表,但又和我们平常使用的表有点儿不一样,这种由子查询结果集组成的表称之为派生表。

3.1.2 WHERE或IN子句中

如:SELECT * FROM t1 WHERE m1 = (SELECT MIN(m2) FROM t2);

SELECT * FROM t1 WHERE m1 IN (SELECT m2 FROM t2);

其他的还有 SELECT 子句中,ORDER BY 子句中,GROUP BY 子句中,虽然语法支持,但没啥意义,就不唠叨这些情况了。

3.2 分类

3.2.1 按返回的结果集区分

标量子查询,只返回一个单一值的子查询称之为标量子查询,比如:

SELECT * FROM t1 WHERE m1 = (SELECT m1 FROM t1 LIMIT 1);

行子查询,就是只返回一条记录的子查询,不过这条记录需要包含多个列(只包含一个列就成了标量子查询了)。比如:SELECT * FROM t1 WHERE (m1, n1) = (SELECT m2, n2 FROM t2 LIMIT 1);

列子查询,就是只返回一个列的数据,不过这个列的数据需要包含多条记录(只包含一条记录就成了标量子查询了)。比如:SELECT * FROM t1 WHERE m1 IN (SELECT m2 FROM t2);

表子查询,就是子查询的结果既包含很多条记录,又包含很多个列,比如:

SELECT * FROM t1 WHERE (m1, n1) IN (SELECT m2, n2 FROM t2);

其中的 (SELECT m2, n2 FROM t2) 就是一个表子查询,这里需要和行子查询对比一下,行子查询中我们用了 LIMIT 1 来保证子查询的结果只有一条记录。

3.2.2 按与外层查询关系来区分

不相关子查询,就是子查询可以单独运行出结果,而不依赖于外层查询的值,我们就可以把这个子查询称之为不相关子查询。
相关子查询,就是需要依赖于外层查询的值的子查询称之为相关子查询。比如:SELECT * FROM t1 WHERE m1 IN (SELECT m2 FROM t2 WHERE n1 = n2);

04子查询在MySQL中是怎么执行的

4.1 标量子查询、行子查询的执行方式

4.1.1 不相关子查询

如下边这个查询语句:

mysql root@localhost:test> explain select * from t1 where t1_a = (select t2_a from t2 limit 1);
+----+-------------+-------+-------+---------------+-------+---------+--------+------+-------------+
| id | select_type | table | type  | possible_keys | key   | key_len | ref    | rows | Extra       |
+----+-------------+-------+-------+---------------+-------+---------+--------+------+-------------+
| 1  | PRIMARY     | t1    | ref   | idx_a         | idx_a | 5       | const  | 1    | Using where |
| 2  | SUBQUERY    | t2    | index | <null>        | idx_a | 5       | <null> | 1000 | Using index |
+----+-------------+-------+-------+---------------+-------+---------+--------+------+-------------+

它的执行方式:

先单独执行 (select t2_a from t2 limit 1) 这个子查询。

然后在将上一步子查询得到的结果当作外层查询的参数再执行外层查询 select * from t1 where t1_a = ...。

也就是说,对于包含不相关的标量子查询或者行子查询的查询语句来说,MySQL 会分别独立的执行外层查询和子查询,就当作两个单表查询就好了。

4.1.2 相关的子查询

比如下边这个查询:

mysql root@localhost:test> explain select * from t1 where t1_a = (select t2_a from t2 where t1.t1_b=t2.t2_b  limit 1);
+----+--------------------+-------+------+---------------+--------+---------+--------+------+-------------+
| id | select_type        | table | type | possible_keys | key    | key_len | ref    | rows | Extra       |
+----+--------------------+-------+------+---------------+--------+---------+--------+------+-------------+
| 1  | PRIMARY            | t1    | ALL  | <null>        | <null> | <null>  | <null> | 100  | Using where |
| 2  | DEPENDENT SUBQUERY | t2    | ALL  | <null>        | <null> | <null>  | <null> | 1000 | Using where |
+----+--------------------+-------+------+---------------+--------+---------+--------+------+-------------+

它的执行方式就是这样的:

先从外层查询中获取一条记录,本例中也就是先从 t1 表中获取一条记录。

然后从上一步骤中获取的那条记录中找出子查询中涉及到的值,就是 t1 表中找出 t1.t1_b 列的值,然后执行子查询。

最后根据子查询的查询结果来检测外层查询 WHERE 子句的条件是否成立,如果成立,就把外层查询的那条记录加入到结果集,否则就丢弃。

然后重复以上步骤,直到 t1 中的记录全部匹配完。

4.2 IN子查询

4.2.1 物化

如果子查询的结果集中的记录条数很少,那么把子查询和外层查询分别看成两个单独的单表查询效率还是蛮高的,但是如果单独执行子查询后的结果集太多的话,就会导致这些问题:

结果集太多,可能内存中都放不下~

对于外层查询来说,如果子查询的结果集太多,那就意味着 IN 子句中的参数特别多,这就导致:

1)无法有效的使用索引,只能对外层查询进行全表扫描。

2)在对外层查询执行全表扫描时,由于 IN 子句中的参数太多,这会导致检测一条记录是否符合和 IN 子句中的参数匹配花费的时间太长。

于是就有:不直接将不相关子查询的结果集当作外层查询的参数,而是将该结果集写入一个临时表里。写入临时表的过程是这样的:

该临时表的列就是子查询结果集中的列。

写入临时表的记录会被去重,让临时表变得更小,更省地方。

一般情况下子查询结果集不大时,就会为它建立基于内存的使用 Memory 存储引擎的临时表,而且会为该表建立哈希索引。

如果子查询的结果集非常大,超过了系统变量 tmp_table_size或者 max_heap_table_size,临时表会转而使用基于磁盘的存储引擎来保存结果集中的记录,索引类型也对应转变为 B+ 树索引。

这个将子查询结果集中的记录保存到临时表的过程称之为物化(Materialize)。为了方便起见,我们就把那个存储子查询结果集的临时表称之为物化表。正因为物化表中的记录都建立了索引(基于内存的物化表有哈希索引,基于磁盘的有 B+ 树索引),通过索引执行IN语句判断某个操作数在不在子查询结果集中变得非常快,从而提升了子查询语句的性能。

mysql root@localhost:test> explain select * from t3 where t3_a in (select t2_a from t2);
+----+--------------+-------------+--------+---------------+------------+---------+--------------+------+-------------+
| id | select_type  | table       | type   | possible_keys | key        | key_len | ref          | rows | Extra       |
+----+--------------+-------------+--------+---------------+------------+---------+--------------+------+-------------+
| 1  | SIMPLE       | t3          | ALL    | idx_a         | <null>     | <null>  | <null>       | 1000 | Using where |
| 1  | SIMPLE       | <subquery2> | eq_ref | <auto_key>    | <auto_key> | 5       | test.t3.t3_a | 1    | <null>      |
| 2  | MATERIALIZED | t2          | index  | idx_a         | idx_a      | 5       | <null>       | 1000 | Using index |
+----+--------------+-------------+--------+---------------+------------+---------+--------------+------+-------------+

其实上边的查询就相当于表 t3 和子查询物化表进行内连接:

mysql root@localhost:test> explain select * from t3 left join t2 on t3.t3_a=t2.t2_a;
+----+-------------+-------+------+---------------+--------+---------+--------------+------+--------+
| id | select_type | table | type | possible_keys | key    | key_len | ref          | rows | Extra  |
+----+-------------+-------+------+---------------+--------+---------+--------------+------+--------+
| 1  | SIMPLE      | t3    | ALL  | <null>        | <null> | <null>  | <null>       | 1000 | <null> |
| 1  | SIMPLE      | t2    | ref  | idx_a         | idx_a  | 5       | test.t3.t3_a | 1    | <null> |
+----+-------------+-------+------+---------------+--------+---------+--------------+------+--------+

此时 MySQL 查询优化器会通过运算来选择成本更低的方案来执行查询。

虽然,上面通过物化表的方式,将IN子查询转换成了联接查询,但还是会有建立临时表的成本,能不能不进行物化操作直接把子查询转换为连接呢?直接转换肯定不行。
-- 这里我们先构造了3条记录,其实也是构造不唯一的普通索引

+------+------+------+
| id   | t2_a | t2_b |
+------+------+------+
| 1100 | 1000 | 1000 |
| 1101 | 1000 | 1000 |
| 1102 | 1000 | 1000 |
+------+------+------+
-- 加限制条件where t2.id>=1100是为了减少要显示的数据
mysql root@localhost:test> select * from t3 where t3_a in (select t2_a from t2 where t2.id>=1100);
+-----+------+------+
| id  | t3_a | t3_b |
+-----+------+------+
| 101 | 1000 | 101  |
+-----+------+------+
1 row in set
Time: 0.016s
mysql root@localhost:test> select * from t3 left join t2 on t3.t3_a=t2.t2_a where t2.id>=1100;
+-----+------+------+------+------+------+
| id  | t3_a | t3_b | id   | t2_a | t2_b |
+-----+------+------+------+------+------+
| 101 | 1000 | 101  | 1100 | 1000 | 1000 |
| 101 | 1000 | 101  | 1101 | 1000 | 1000 |
| 101 | 1000 | 101  | 1102 | 1000 | 1000 |
+-----+------+------+------+------+------+
3 rows in set
Time: 0.018s

所以说 IN 子查询和表联接之间并不完全等价。而我们需要的是另一种叫做半联接 (semi-join) 的联接方式 :对于 t3 表的某条记录来说,我们只关心在 t2 表中是否存在与之匹配的记录,而不关心具体有多少条记录与之匹配,最终的结果集中也只保留 t3 表的记录。

注意:semi-join 只是在 MySQL 内部采用的一种执行子查询的方式,MySQL 并没有提供面向用户的 semi-join 语法。

4.2.2 半联接的实现:

  • Table pullout (子查询中的表上拉)

当子查询的查询列表处只有主键或者唯一索引列时,可以直接把子查询中的表上拉到外层查询的 FROM 子句中,并把子查询中的搜索条件合并到外层查询的搜索条件中,比如这个:

mysql root@localhost:test> select * from t3 where t3_a in (select t2_a from t2 where t2.id=999)
+-----+------+------+
| id  | t3_a | t3_b |
+-----+------+------+
| 102 | 999  | 102  |
+-----+------+------+
1 row in set
Time: 0.024s
mysql root@localhost:test> select * from t3 join t2 on t3.t3_a=t2.t2_a where t2.id=999;
+-----+------+------+-----+------+------+
| id  | t3_a | t3_b | id  | t2_a | t2_b |
+-----+------+------+-----+------+------+
| 102 | 999  | 102  | 999 | 999  | 999  |
+-----+------+------+-----+------+------+
1 row in set
Time: 0.028s
mysql root@localhost:test> explain select * from t3 where t3_a in (select t2_a from t2 where t2.id=999)
+----+-------------+-------+-------+---------------+---------+---------+-------+------+--------+
| id | select_type | table | type  | possible_keys | key     | key_len | ref   | rows | Extra  |
+----+-------------+-------+-------+---------------+---------+---------+-------+------+--------+
| 1  | SIMPLE      | t2    | const | PRIMARY,idx_a | PRIMARY | 4       | const | 1    | <null> |
| 1  | SIMPLE      | t3    | ref   | idx_a         | idx_a   | 5       | const | 1    | <null> |
+----+-------------+-------+-------+---------------+---------+---------+-------+------+--------+
  • FirstMatch execution strategy (首次匹配)

FirstMatch 是一种最原始的半连接执行方式,跟相关子查询的执行方式是一样的,就是说先取一条外层查询的中的记录,然后到子查询的表中寻找符合匹配条件的记录,如果能找到一条,则将该外层查询的记录放入最终的结果集并且停止查找更多匹配的记录,如果找不到则把该外层查询的记录丢弃掉。然后再开始取下一条外层查询中的记录,重复上边这个过程。

mysql root@localhost:test> explain select * from t3 where t3_a in (select t2_a from t2 where t2.t2_a=1000)
+----+-------------+-------+------+---------------+-------+---------+-------+------+-----------------------------+
| id | select_type | table | type | possible_keys | key   | key_len | ref   | rows | Extra                       |
+----+-------------+-------+------+---------------+-------+---------+-------+------+-----------------------------+
| 1  | SIMPLE      | t3    | ref  | idx_a         | idx_a | 5       | const | 1    | <null>                      |
| 1  | SIMPLE      | t2    | ref  | idx_a         | idx_a | 5       | const | 4    | Using index; FirstMatch(t3) |
+----+-------------+-------+------+---------------+-------+---------+-------+------+-----------------------------+
  • DuplicateWeedout execution strategy (重复值消除)

转换为半连接查询后,t3 表中的某条记录可能在 t2 表中有多条匹配的记录,所以该条记录可能多次被添加到最后的结果集中,为了消除重复,我们可以建立一个临时表,并设置主键id,每当某条 t3 表中的记录要加入结果集时,就首先把这条记录的id值加入到这个临时表里,如果添加成功,说明之前这条 t2 表中的记录并没有加入最终的结果集,是一条需要的结果;如果添加失败,说明之前这条 s1 表中的记录已经加入过最终的结果集,直接把它丢弃。

  • LooseScan execution strategy (松散扫描)

这种虽然是扫描索引,但只取值相同的记录的第一条去做匹配操作的方式称之为松散扫描。

4.2.3 半联接的适用条件

当然,并不是所有包含IN子查询的查询语句都可以转换为 semi-join,只有形如这样的查询才可以被转换为 semi-join:

SELECT ... FROM outer_tables 
    WHERE expr IN (SELECT ... FROM inner_tables ...) AND ...

-- 或者这样的形式也可以:

SELECT ... FROM outer_tables 
    WHERE (oe1, oe2, ...) IN (SELECT ie1, ie2, ... FROM inner_tables ...) AND ...

用文字总结一下,只有符合下边这些条件的子查询才可以被转换为 semi-join:

  1. 该子查询必须是和IN语句组成的布尔表达式,并且在外层查询的 WHERE 或者 ON 子句中出现
  2. 外层查询也可以有其他的搜索条件,只不过和 IN 子查询的搜索条件必须使用AND 连接起来
  3. 该子查询必须是一个单一的查询,不能是由若干查询由 UNION 连接起来的形式
  4. 该子查询不能包含 GROUP BY 或者 HAVING 语句或者聚集函数

4.2.4 转为 EXISTS 子查询

不管子查询是相关的还是不相关的,都可以把 IN 子查询尝试转为 EXISTS子查询。其实对于任意一个 IN 子查询来说,都可以被转为 EXISTS 子查询,通用的例子如下:

outer_expr IN (SELECT inner_expr FROM ... WHERE subquery_where)
-- 可以被转换为:
EXISTS (SELECT inner_expr FROM ... WHERE subquery_where AND outer_expr=inner_expr)

当然这个过程中有一些特殊情况,比如在 outer_expr 或者 inner_expr 值为 NULL 的情况下就比较特殊。因为有 NULL 值作为操作数的表达式结果往往是 NULL,比方说:

mysql root@localhost:test> SELECT NULL IN (1, 2, 3);
+-------------------+
| NULL IN (1, 2, 3) |
+-------------------+
| <null>            |
+-------------------+
1 row in set

而 EXISTS 子查询的结果肯定是 TRUE 或者 FASLE 。但是现实中我们大部分使用 IN 子查询的场景是把它放在 WHERE 或者 ON 子句中,而 WHERE 或者 ON 子句是不区分 NULL 和 FALSE 的,比方说:

mysql root@localhost:test> SELECT 1 FROM s1 WHERE NULL;
+---+
| 1 |
+---+
0 rows in set
Time: 0.016s
mysql root@localhost:test> SELECT 1 FROM s1 WHERE FALSE;
+---+
| 1 |
+---+
0 rows in set
Time: 0.033s

所以只要我们的IN子查询是放在 WHERE 或者 ON 子句中的,那么 IN ->  EXISTS 的转换就是没问题的。说了这么多,为啥要转换呢?这是因为不转换的话可能用不到索引,比方说下边这个查询:

mysql root@localhost:test> explain select * from t3 where t3_a in (select t2_a from t2 where t2.t2_a>=999) or t3_b > 1000;
+----+-------------+-------+-------+---------------+--------+---------+--------+------+--------------------------+
| id | select_type | table | type  | possible_keys | key    | key_len | ref    | rows | Extra                    |
+----+-------------+-------+-------+---------------+--------+---------+--------+------+--------------------------+
| 1  | PRIMARY     | t3    | ALL   | <null>        | <null> | <null>  | <null> | 1000 | Using where              |
| 2  | SUBQUERY    | t2    | range | idx_a         | idx_a  | 5       | <null> | 107  | Using where; Using index |
+----+-------------+-------+-------+---------------+--------+---------+--------+------+--------------------------+

但是将它转为 EXISTS 子查询后却可以使用到索引:

mysql root@localhost:test> explain select * from t3 where exists (select 1 from t2 where t2.t2_a>=999 and t2.t2_a=t3.t3_a) or t3_b > 1000;
+----+--------------------+-------+------+---------------+--------+---------+--------------+------+--------------------------+
| id | select_type        | table | type | possible_keys | key    | key_len | ref          | rows | Extra                    |
+----+--------------------+-------+------+---------------+--------+---------+--------------+------+--------------------------+
| 1  | PRIMARY            | t3    | ALL  | <null>        | <null> | <null>  | <null>       | 1000 | Using where              |
| 2  | DEPENDENT SUBQUERY | t2    | ref  | idx_a         | idx_a  | 5       | test.t3.t3_a | 1    | Using where; Using index |
+----+--------------------+-------+------+---------------+--------+---------+--------------+------+--------------------------+

需要注意的是,如果 IN 子查询不满足转换为 semi-join 的条件,又不能转换为物化表或者转换为物化表的成本太大,那么它就会被转换为 EXISTS 查询。或者转换为物化表的成本太大,那么它就会被转换为 EXISTS 查询。

05总结

1. 如果IN子查询符合转换为 semi-join 的条件,查询优化器会优先把该子查询转换为 semi-join,然后再考虑下边执行半连接的策略中哪个成本最低,

1)Table pullout

2)DuplicateWeedout

3)LooseScan

4)FirstMatch

选择成本最低的那种执行策略来执行子查询。

2. 如果IN子查询不符合转换为 semi-join 的条件,那么查询优化器会从下边两种策略中找出一种成本更低的方式执行子查询:

1)先将子查询物化之后再执行查询

2)执行 IN to EXISTS 转换

到此这篇关于MySQL子查询原理的文章就介绍到这了,更多相关MySQL子查询原理内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 详解MySql基本查询、连接查询、子查询、正则表达查询

    查询数据指从数据库中获取所需要的数据.查询数据是数据库操作中最常用,也是最重要的操作.用户可以根据自己对数据的需求,使用不同的查询方式.通过不同的查询方式,可以获得不同的数据.MySQL中是使用SELECT语句来查询数据的.在这一章中将讲解的内容包括. 1.查询语句的基本语法 2.在单表上查询数据 3.使用聚合函数查询数据 4.多表上联合查询 5.子查询 6.合并查询结果 7.为表和字段取别名 8.使用正则表达式查询 什么是查询? 怎么查的? 数据的准备如下: create table STUD

  • MySQL优化之使用连接(join)代替子查询

    使用连接(JOIN)来代替子查询(Sub-Queries) MySQL从4.1开始支持SQL的子查询.这个技术可以使用SELECT语句来创建一个单列的查询结果,然后把这个结果作为过滤条件用在另一个查询中.例如,我们要将客户基本信息表中没有任何订单的客户删除掉,就可以利用子查询先从销售信息表中将所有发出订单的客户ID取出来,然后将结果传递给主查询,如下所示: DELETE FROM customerinfo WHERE CustomerID NOT in (SELECT CustomerID FR

  • MySQL里面的子查询实例

    一,子选择基本用法 1,子选择的定义 子迭择允许把一个查询嵌套在另一个查询当中.比如说:一个考试记分项目把考试事件分为考试(T)和测验(Q)两种情形.下面这个查询就能只找出学生们的考试成绩 select * from score where event_id in (select event_id from event where type='T'); 2,子选择的用法(3种)         用子选择来生成一个参考值 在这种情况下,用内层的查询语句来检索出一个数据值,然后把这个数据值用在外层

  • MySQL子查询的几种常见形式介绍

    mysql子查询的几种常见写法: 复制代码 代码如下: select * from xxx where col = [any|all](select * from xxxx); 该句法可分为加关键词和不加关键词的写法,当不加关键词的时候,子查询语句返回的是一个离散值(注意是一个),查询语句将以子查询语句的结果作为自己 where子句的条件进行查询,该句法可以在子查询语句前加入any.all.some等关键字,此时子查询语句返回的是一组离散值.any则表示,查询语句是以子查询返回的值作为一个范围,

  • 浅谈MySQL中的子查询优化技巧

    mysql的子查询的优化一直不是很友好,一直有受业界批评比较多,也是我在sql优化中遇到过最多的问题之一,你可以点击这里 ,这里来获得一些信息,mysql在处理子查询的时候,会将子查询改写,通常情况下,我们希望由内到外,也就是先完成子查询的结果,然后在用子查询来驱动外查询的表,完成查询,但是恰恰相反,子查询不会先被执行:今天希望通过介绍一些实际的案例来加深对mysql子查询的理解: 案例:用户反馈数据库响应较慢,许多业务动更新被卡住:登录到数据库中观察,发现长时间执行的sql: | 10437

  • 详细讲述MySQL中的子查询操作

    继续做以下的前期准备工作: 新建一个测试数据库TestDB: create database TestDB; 创建测试表table1和table2: CREATE TABLE table1 ( customer_id VARCHAR(10) NOT NULL, city VARCHAR(10) NOT NULL, PRIMARY KEY(customer_id) )ENGINE=INNODB DEFAULT CHARSET=UTF8; CREATE TABLE table2 ( order_id

  • mysql连接查询、联合查询、子查询原理与用法实例详解

    本文实例讲述了mysql连接查询.联合查询.子查询原理与用法.分享给大家供大家参考,具体如下: 本文内容: 连接查询 联合查询 子查询 from子查询 where子查询 exists子查询 首发日期:2018-04-11 连接查询: 连接查询就是将多个表联合起来查询,连接查询方式有内连接.外连接.自然连接.交叉连接.连接查询使得可以同时查看多张表中数据. 内连接:有条件连接,多个表之间依据指定条件连接,匹配结果是保留符合匹配结果的记录. 外连接:与内连接不同的是不管匹配符不符合都保留,根据外连接

  • MySQL笔记之子查询使用介绍

    子查询是将一个查询语句嵌套在另一个查询语句中 内层查询语句的查询结果,可以为外层查询语句提供查询条件 因为在特定情况下,一个查询语句的条件需要另一个查询语句来获取 参考表:employee 参考表:department 带IN关键字的子查询 复制代码 代码如下: mysql> SELECT * FROM employee    -> WHERE d_id IN    -> (SELECT d_id FROM department);+------+------+--------+----

  • MySQL子查询原理的深入分析

    目录 01前言 02准备内容 03子查询的语法形式和分类 3.1 语法形式 3.1.1  FROM子句中 3.1.2 WHERE或IN子句中 3.2 分类 3.2.1 按返回的结果集区分 3.2.2 按与外层查询关系来区分 04子查询在MySQL中是怎么执行的 4.1 标量子查询.行子查询的执行方式 4.1.1 不相关子查询 4.1.2 相关的子查询 4.2 IN子查询 4.2.1 物化 4.2.2 半联接的实现: 4.2.3 半联接的适用条件 4.2.4 转为 EXISTS 子查询 05总结

  • MySql 缓存查询原理与缓存监控和索引监控介绍

    查询缓存 1.查询缓存操作原理 mysql执行查询语句之前,把查询语句同查询缓存中的语句进行比较,且是按字节比较,仅完全一致才被认为相同.如下,这两条语句被视为不同的查询 SELECT * FROM tb1_name Select * from tb1_name 1)不同数据库.不同协议版本,或字符集不同的查询被视为不同的查询并单独缓存. 2)以下两种类型的查询不被缓存 a.预处理语句 b.嵌套查询的子查询 3)从查询缓存抓取查询结果前,mysql检查用户对查询涉及的所有数据库和表是否有查询权限

  • MySql子查询IN的执行和优化的实现

    目录 IN为什么慢? IN和EXISTS哪个快? 如何提高效率? MySQL5.6对子查询的优化? SEMI JOIN策略 Duplicate Weedout优化 Materialization优化 FirstMacth优化 LooseScan优化 SEMI JOIN变量 参考 IN为什么慢? 在应用程序中使用子查询后,SQL语句的查询性能变得非常糟糕.例如: SELECT driver_id FROM driver where driver_id in (SELECT driver_id FR

  • MYSQL子查询和嵌套查询优化实例解析

    查询游戏历史成绩最高分前100 Sql代码 SELECT ps.* FROM cdb_playsgame ps WHERE ps.credits=(select MAX(credits) FROM cdb_playsgame ps1 where ps.uid=ps1.uid AND ps.gametag=ps1.gametag) AND ps.gametag='yeti3' GROUP BY ps.uid order by ps.credits desc LIMIT 100; Sql代码 SEL

  • MySQL子查询用法实例分析

    本文实例讲述了MySQL子查询用法.分享给大家供大家参考,具体如下: 假设表my_tbl包含三个字段a,b,c:现在需要查询表中列a的每个不同值下的列b为最小值的记录量. 比如表记录为: a  b  c 1  3  'cd' 2  3  'nhd' 1  5  'bg' 2  6  'cds' 1  7  'kiy' 3  7  'vsd' 3  8  'ndf' 希望得到结果为: a  b  c 1  3  'cd' 2  3  'nhd' 3  7  'vsd' (1) 其中一个做法:先查出

  • MySQL子查询操作实例详解

    本文实例总结了MySQL子查询操作.分享给大家供大家参考,具体如下: 定义两个表tb1和tb2 CREATE table tbl1 ( num1 INT NOT NULL); CREATE table tbl2 ( num2 INT NOT NULL); 向两个表中插入数据: INSERT INTO tbl1 values(1), (5), (13), (27); INSERT INTO tbl2 values(6), (14), (11), (20); any some关键字的子查询 SELE

  • 详解MySQL子查询(嵌套查询)、联结表、组合查询

    一.子查询 MySQL 4.1版本及以上支持子查询 子查询:嵌套在其他查询中的查询. 子查询的作用: 1.进行过滤: 实例1:检索订购物品TNT2的所有客户的ID = + 一般,在WHERE子句中对于能嵌套的子查询的数目没有限制,不过在实际使用时由于性能的限制,不能嵌套太多的子查询. 注意:列必须匹配 --在WHERE子句中使用子查询(如这里所示),应该保证SELECT语句具有与WHERE子句中相同数目的列.通常,子查询将返回单个列并且与单个列匹配,但如果需要也可以使用多个列. 示例2:返回订购

  • 实例详解mysql子查询

    子查询分类 按返回结果集分类 子查询按返回结果集的不同分为4种:表子查询,行子查询,列子查询和标量子查询. 表子查询:返回的结果集是一个行的集合,N行N列(N>=1).表子查询经常用于父查询的FROM子句中. 行子查询:返回的结果集是一个列的集合,一行N列(N>=1).行子查询可以用于福查询的FROM子句和WHERE子句中. 列子查询:返回的结果集是一个行的集合,N行一列(N>=1). 标量子查询:返回的结果集是一个标量集合,一行一列,也就是一个标量值.可以指定一个标量表达式的任何地方,

随机推荐