MySQL 子查询优化[IN/EXISTS]--smei join

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS PostgreSQL,集群系列 2核4GB
简介: MySQL 里面有哪些子查询呢? 标量子查询 内联视图 半连接/反连接 本篇主要讲解半连接查询 半连接?可以这么理解 where 条件后面有In/EXISTS这样的子查询称为semi jion 格式:select .

MySQL 里面有哪些子查询呢?

  • 标量子查询
  • 内联视图
  • 半连接/反连接
  • 本篇主要讲解半连接查询
    半连接?可以这么理解 where 条件后面有In/EXISTS这样的子查询称为semi jion

格式:select ..... from outer_tables where expr in (select .... from inner_tables ...) and ...

  • 为什么要用semi join来进行优化子查询?

    • 因为where后面的子查询每扫描一条数据,Where子查询都会被重新执行一遍,这样效率就会很低如果父表数据很多带来什么问题?那么就有了将子查询的结果提升到FROM中,不需要再父表中每个符合条件的数据都要去把子查询执行一轮了。
  • MySQL又是需要满足什么条件才会转换成semi jion?

    • 子查询是in or = any , 不可以是not in
    • 子查询只能包含一个Query bolock, 不可以有union等操作
    • 子查询不能包含group by 或者having
    • 不能包含聚合函数
    • 子查询的谓词是where子句的一部分
    • 子查询谓词不可以是外部查询条件或者否定查询条件
    • 不可以包含Straight_join 限定词
    • 只能用于select insert,而update,delete则都不可用
  • 有哪些因为可以将半连接和常规连接进行区分?

    • 在semi-join 中内部表不会在结果中造成重复
    • 内部table中没有列添加到操作结果中。
    • 这意味着半连接的结果是外表行中的子集。这也意味着大部分的半连接的特殊处理是关于内部表中有效的消除重复

那么我们了解了为什么有semi jion,满足什么条件转换成semi join以及如何区分,那对于semi jion 又有哪些优化策略呢?
--因为半连接是一种常规连接操作,并结合从半连接内部表中删除可能的重复项。
MySQL实现了四种不同的半连接执行策略,它们有不同的删除重复项的方法:

  1. FirstMatch
  2. DuplicateWeedout
  3. Materialization
  4. LooseScan
  • FirstMatch:

当扫描inner table 来组合数据时,并且有多个符合条件的数据时,只选择第一条满足条件的记录,连接后的结果,存与临时表。

EG:

select * 
        from country 
    where country.code in 
        ( select city.CountryCode  
             from city    
         where    city.Population >1*1000*1000)
    and Country.Continent='Europe';

image
由于Germany有两个大城市(在该图中),它将被放入查询输出两次。 这是不正确的,SELECT ... FROM Country不应该产生两次相同的国家记录。 FirstMatch策略避免了一旦找到第一次真正的匹配就通过快速执行生成重复项:
image

dba_jingjing@3306>[world]>desc
    -> select *
    ->     from country
    -> where country.code in
    ->     ( select city.CountryCode
    ->          from city
    ->      where    city.Population >1*100)
    -> and Country.Continent='Europe';
+----+-------------+---------+------------+------+----------------------------+---------------+---------+--------------------+------+----------+----------------------------------+
| id | select_type | table   | partitions | type | possible_keys              | key           | key_len | ref                | rows | filtered | Extra                            |
+----+-------------+---------+------------+------+----------------------------+---------------+---------+--------------------+------+----------+----------------------------------+
|  1 | SIMPLE      | country | NULL       | ref  | PRIMARY,idx_Continent      | idx_Continent | 4       | const              |   46 |   100.00 | Using where                      |
|  1 | SIMPLE      | city    | NULL       | ref  | CountryCode,idx_Population | CountryCode   | 3       | world.country.Code |   18 |    97.91 | Using where; FirstMatch(country) |
+----+-------------+---------+------------+------+----------------------------+---------------+---------+--------------------+------+----------+----------------------------------+
2 rows in set, 1 warning (0.03 sec)

如果没有开启simi jion 的方式下运行:

dba_jingjing@3306>[world]>desc
    -> select *
    ->     from country
    -> where country.code in
    ->     ( select city.CountryCode
    ->          from city
    ->      where    city.Population >1*100)
    -> and Country.Continent='Europe';
+----+--------------------+---------+------------+----------------+----------------------------+---------------+---------+-------+------+----------+-------------+
| id | select_type        | table   | partitions | type           | possible_keys              | key           | key_len | ref   | rows | filtered | Extra       |
+----+--------------------+---------+------------+----------------+----------------------------+---------------+---------+-------+------+----------+-------------+
|  1 | PRIMARY            | country | NULL       | ref            | idx_Continent              | idx_Continent | 4       | const |   46 |   100.00 | Using where |
|  2 | DEPENDENT SUBQUERY | city    | NULL       | index_subquery | CountryCode,idx_Population | CountryCode   | 3       | func  |   18 |    97.91 | Using where |
+----+--------------------+---------+------------+----------------+----------------------------+---------------+---------+-------+------+----------+-------------+
2 rows in set, 1 warning (0.03 sec)

  • DuplicateWeedout :

先和子查询做简单的inner join 操作,并使用临时表(建有Primary key)来消除重复记录。

select * 
from country 
where country.code in ( select city.CountryCode 
                       from city  
                       where 
                           Population >0.33 * country.Population and 
                           city.Population >1*1000*1000);

首先做inner join 操作:
image
内部连接产生重复项。 Germany有三次big city,此时将DuplicateWeedout策略进行应用:
image

dba_jingjing@3306>[world]>desc select * from country where country.code in ( select city.CountryCode from city  where Population >0.33 * country.Population and city.Population >1*1000*1000);
+----+-------------+---------+------------+--------+----------------------------+----------------+---------+------------------------+------+----------+----------------------------------------+
| id | select_type | table   | partitions | type   | possible_keys              | key            | key_len | ref                    | rows | filtered | Extra                                  |
+----+-------------+---------+------------+--------+----------------------------+----------------+---------+------------------------+------+----------+----------------------------------------+
|  1 | SIMPLE      | city    | NULL       | range  | CountryCode,idx_Population | idx_Population | 4       | NULL                   |  237 |   100.00 | Using index condition; Start temporary |
|  1 | SIMPLE      | country | NULL       | eq_ref | PRIMARY                    | PRIMARY        | 3       | world.city.CountryCode |    1 |   100.00 | Using where; End temporary             |
+----+-------------+---------+------------+--------+----------------------------+----------------+---------+------------------------+------+----------+----------------------------------------+
2 rows in set, 2 warnings (0.03 sec)


dba_jingjing@3306>[world]>show warnings\G
*************************** 1. row ***************************
  Level: Note
   Code: 1276
Message: Field or reference 'world.country.Population' of SELECT #2 was resolved in SELECT #1
*************************** 2. row ***************************
  Level: Note
   Code: 1003
Message: /* select#1 */ select `world`.`country`.`Code` AS `Code`,`world`.`country`.`Name` AS `Name`,`world`.`country`.`Continent` AS `Continent`,`world`.`country`.`Region` AS `Region`,`world`.`country`.`SurfaceArea` AS `SurfaceArea`,`world`.`country`.`IndepYear` AS `IndepYear`,`world`.`country`.`Population` AS `Population`,`world`.`country`.`LifeExpectancy` AS `LifeExpectancy`,`world`.`country`.`GNP` AS `GNP`,`world`.`country`.`GNPOld` AS `GNPOld`,`world`.`country`.`LocalName` AS `LocalName`,`world`.`country`.`GovernmentForm` AS `GovernmentForm`,`world`.`country`.`HeadOfState` AS `HeadOfState`,`world`.`country`.`Capital` AS `Capital`,`world`.`country`.`Code2` AS `Code2` from `world`.`country` semi join (`world`.`city`) where ((`world`.`country`.`Code` = `world`.`city`.`CountryCode`) and (`world`.`city`.`Population` > (0.33 * `world`.`country`.`Population`)) and (`world`.`city`.`Population` > <cache>(((1 * 1000) * 1000))))
2 rows in set (0.03 sec)

该查询将读取City表中的237行,并且它们中的每个都将在Country表中进行主键查找,从而得到另外237行。 这总共提供了474行,并且您需要在临时表中添加237个查找。
那么关闭semi join:

dba_jingjing@3306>[world]>desc select * from country where country.code in ( select city.CountryCode from city  where Population >0.33 * country.Population and city.Population >1*1000*1000);
+----+--------------------+---------+------------+----------------+---------------+-------------+---------+------+------+----------+-------------+
| id | select_type        | table   | partitions | type           | possible_keys | key         | key_len | ref  | rows | filtered | Extra       |
+----+--------------------+---------+------------+----------------+---------------+-------------+---------+------+------+----------+-------------+
|  1 | PRIMARY            | country | NULL       | ALL            | NULL          | NULL        | NULL    | NULL |  239 |   100.00 | Using where |
|  2 | DEPENDENT SUBQUERY | city    | NULL       | index_subquery | CountryCode   | CountryCode | 3       | func |   18 |    11.11 | Using where |
+----+--------------------+---------+------------+----------------+---------------+-------------+---------+------+------+----------+-------------+
2 rows in set, 2 warnings (0.03 sec)

这个执行计划将会读取到(239+239 * 18)=4541行数据,这个会比较慢。

--另外两种策略下次分享

图片来源MariaDB官网

相关实践学习
如何快速连接云数据库RDS MySQL
本场景介绍如何通过阿里云数据管理服务DMS快速连接云数据库RDS MySQL,然后进行数据表的CRUD操作。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
20天前
|
SQL 关系型数据库 MySQL
深入解析MySQL的EXPLAIN:指标详解与索引优化
MySQL 中的 `EXPLAIN` 语句用于分析和优化 SQL 查询,帮助你了解查询优化器的执行计划。本文详细介绍了 `EXPLAIN` 输出的各项指标,如 `id`、`select_type`、`table`、`type`、`key` 等,并提供了如何利用这些指标优化索引结构和 SQL 语句的具体方法。通过实战案例,展示了如何通过创建合适索引和调整查询语句来提升查询性能。
119 9
|
3天前
|
SQL 存储 关系型数据库
MySQL秘籍之索引与查询优化实战指南
最左前缀原则。不冗余原则。最大选择性原则。所谓前缀索引,说白了就是对文本的前几个字符建立索引(具体是几个字符在建立索引时去指定),比如以产品名称的前 10 位来建索引,这样建立起来的索引更小,查询效率更快!
45 22
 MySQL秘籍之索引与查询优化实战指南
|
1天前
|
SQL 关系型数据库 MySQL
MySQL派生表合并优化的原理和实现
通过本文的详细介绍,希望能帮助您理解和实现MySQL中派生表合并优化,提高数据库查询性能。
15 7
|
25天前
|
缓存 关系型数据库 MySQL
MySQL 索引优化以及慢查询优化
通过本文的介绍,希望您能够深入理解MySQL索引优化和慢查询优化的方法,并在实际应用中灵活运用这些技术,提升数据库的整体性能。
63 18
|
15天前
|
存储 关系型数据库 MySQL
10个案例告诉你mysql不使用子查询的原因
大家好,我是V哥。上周与朋友讨论数据库子查询问题,深受启发。为此,我整理了10个案例,详细说明如何通过优化子查询提升MySQL性能。主要问题包括性能瓶颈、索引失效、查询优化器复杂度及数据传输开销等。解决方案涵盖使用EXISTS、JOIN、IN操作符、窗口函数、临时表及索引优化等。希望通过这些案例,帮助大家在实际开发中选择更高效的查询方式,提升系统性能。关注V哥,一起探讨技术,欢迎点赞支持!
104 5
|
24天前
|
缓存 关系型数据库 MySQL
MySQL 索引优化以及慢查询优化
通过本文的介绍,希望您能够深入理解MySQL索引优化和慢查询优化的方法,并在实际应用中灵活运用这些技术,提升数据库的整体性能。
23 7
|
23天前
|
缓存 关系型数据库 MySQL
MySQL 索引优化与慢查询优化:原理与实践
通过本文的介绍,希望您能够深入理解MySQL索引优化与慢查询优化的原理和实践方法,并在实际项目中灵活运用这些技术,提升数据库的整体性能。
61 5
|
2月前
|
关系型数据库 MySQL Java
MySQL索引优化与Java应用实践
【11月更文挑战第25天】在大数据量和高并发的业务场景下,MySQL数据库的索引优化是提升查询性能的关键。本文将深入探讨MySQL索引的多种类型、优化策略及其在Java应用中的实践,通过历史背景、业务场景、底层原理的介绍,并结合Java示例代码,帮助Java架构师更好地理解并应用这些技术。
59 2
|
15天前
|
存储 Oracle 关系型数据库
数据库传奇:MySQL创世之父的两千金My、Maria
《数据库传奇:MySQL创世之父的两千金My、Maria》介绍了MySQL的发展历程及其分支MariaDB。MySQL由Michael Widenius等人于1994年创建,现归Oracle所有,广泛应用于阿里巴巴、腾讯等企业。2009年,Widenius因担心Oracle收购影响MySQL的开源性,创建了MariaDB,提供额外功能和改进。维基百科、Google等已逐步替换为MariaDB,以确保更好的性能和社区支持。掌握MariaDB作为备用方案,对未来发展至关重要。
39 3
|
15天前
|
安全 关系型数据库 MySQL
MySQL崩溃保险箱:探秘Redo/Undo日志确保数据库安全无忧!
《MySQL崩溃保险箱:探秘Redo/Undo日志确保数据库安全无忧!》介绍了MySQL中的三种关键日志:二进制日志(Binary Log)、重做日志(Redo Log)和撤销日志(Undo Log)。这些日志确保了数据库的ACID特性,即原子性、一致性、隔离性和持久性。Redo Log记录数据页的物理修改,保证事务持久性;Undo Log记录事务的逆操作,支持回滚和多版本并发控制(MVCC)。文章还详细对比了InnoDB和MyISAM存储引擎在事务支持、锁定机制、并发性等方面的差异,强调了InnoDB在高并发和事务处理中的优势。通过这些机制,MySQL能够在事务执行、崩溃和恢复过程中保持
42 3

推荐镜像

更多