>  기사  >  데이터 베이스  >  MySQL 서브쿼리의 원리는 무엇입니까?

MySQL 서브쿼리의 원리는 무엇입니까?

王林
王林앞으로
2023-05-29 08:14:151126검색

    01 서문

    하위 쿼리에서 가장 널리 알려진 설명은 다른 쿼리 문이 쿼리 문에 중첩되어 있다는 것입니다. 일상 업무에서 MySQL을 접하는 학생들은 모두 하위 쿼리를 알고 있거나 사용해 본 적이 있을 것으로 생각합니다. 하지만 쿼리가 얼마나 효율적으로 구현됩니까? 많은 사람들이 이 두 가지 문제에 대해 명확하게 알지 못합니다. 함께.

    02 준비 내용

    이 작업에는 세 개의 테이블이 필요합니다. 이 세 테이블에는 모두 기본 키 인덱스 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 문법 형식

    하위 쿼리의 구문은 하위 쿼리가 외부 쿼리에서 다양한 위치에 나타날 수 있다고 규정하고 있습니다. 여기서는 공통적인 내용만 소개합니다. 각각: FROM 절에서

    3.1.1

    (예: SELECT m, n FROM (SELECT m2 + 1 AS m, n2 AS n FROM t2 WHERE m2 > 2) AS t;<code>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 + 1 AS m, n2 AS n FROM t2 WHERE m2 > 2) FROM 절에 있는 이 하위 쿼리는 a와 동일합니다. 하지만 우리가 일반적으로 사용하는 테이블과는 조금 다릅니다. 하위 쿼리 결과 집합으로 구성된 이 테이블을 파생 테이블이라고 합니다.

    3.1.2
    WHERE 또는 IN 절(예: SELECT * FROM t1 WHERE m1 = (SELECT MIN(m2) FROM t2);


                                             ~                                 IN (t2에서 m2 선택);

    기타에는 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 |
    +----+-------------+-------+-------+---------------+-------+---------+--------+------+-------------+
    실행 방법:

    먼저 이 하위 쿼리를 별도로 실행합니다(t2 제한 1에서 t2_a 선택).

    그런 다음 이전 하위 쿼리의 결과를 외부 쿼리의 매개 변수로 사용한 다음 외부 쿼리 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 절의 조건이 true인지 확인하여 true이면 외부 쿼리의 레코드를 결과 집합에 추가합니다. 폐기됩니다. 🎜🎜그런 다음 t1의 모든 레코드가 일치할 때까지 위 단계를 반복합니다. 🎜🎜4.2 IN subquery🎜🎜4.2.1 Materialization🎜🎜서브 쿼리 결과 집합의 레코드 수가 매우 적다면 하위 쿼리와 외부 쿼리를 두 개의 별도 단일 테이블 쿼리로 처리하는 것이 매우 효율적입니다. 하지만 하위 쿼리만 실행한 후 결과 세트가 너무 많으면 다음과 같은 문제가 발생합니다. 🎜🎜결과 세트가 너무 많아 메모리에 맞지 않을 수 있습니다~🎜🎜외부 쿼리의 경우 하위 쿼리의 결과 세트가 너무 많다는 것은 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 或 FALSE。但是现实中我们大部分使用 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 查询。

    위 내용은 MySQL 서브쿼리의 원리는 무엇입니까?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

    성명:
    이 기사는 yisu.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제