前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >数据库查询优化技术(二):子查询优化

数据库查询优化技术(二):子查询优化

作者头像
sunonzj
发布2022-06-21 14:13:43
3.1K0
发布2022-06-21 14:13:43
举报
文章被收录于专栏:zjblogzjblog

数据库查询优化技术总览

本章内容:

1查询的基本操作

2查询的2种类型

3Query Execution Plan of MySQL

4子查询的优化

5How to optimize SubQuery?

6MySQL可以优化什么格式的子查询?

查询的基本操作

1选择操作

对应的是限制条件(格式类似“field<op>consant”, field表示列对象,op是操作符如"="、">"等)。

操作对象是二维表中的行

优化方式:

选择操作下推

目的:

是尽量减少连接操作前的元素组,使得中间临时关系尽量少(元组数少,连接得到的元组数就少)

好处:

这样可能减少IO和CPU的消耗、节约内存空间。

2投影操作。

对应的SELECT查询的目的列对象。

优化方式:投影操作下推

目的:

是尽量减少连接操作前的列数,使得中间临时关系尽量少(特别注意差别:选择操作是使元祖的个数”尽量少“,投影操作是使一条元祖”尽量小“)

好处:

这样虽然不能减少IO(多数数据库存储方式是行存储,元祖是读取的最基本单位,所以要想操作列则必须读取一行数据),但可以减少连接后的中间关系的元祖大小,节约内存空间。

3连接操作

对应的是连接对象条件(格式类似“field_1<op>field_2”,field_1和field_2表示不同表的列对象,op是操作符如“=”、“>”等),表示两个表连接的条件。

Q:连接操作有优化方式么?

连接操作涉及到的两个子问题

3.1多表连接中每个表被连接的顺序决定着效率

如果一个查询语句只有一个表,则这样的语句很简单;但如果有多个表,则会设计表之间以什么样的顺序连接最高效(如A、B、C三表连接,如果ABC、ACB、BCA等连接之后的结果集一样,则哪种连接次序的效率最高,是需要考虑的问题)。

3.2多表连接每个表被连接的顺序被用户语义决定

查询语句多表连接有着不同的语义(如是笛卡尔集、内连接、还是外连接中的左外连接等),这决定着表之间的前后连接次序是不能随意更换的,否则,结果集中数据是不同的。因此,表的前后连接次序是不能随意交换的。

查询的2种类型

根据SQL语句的形式特点,还可以做如下区分:

1针对SPJ的查询优化。

基于选择、投影、连接三种基本操作相结合的查询所做的优化。

2针对非SPJ的查询优化

在SPJ基础上存在GROUPBY操作的查询,这是一种较为复杂的查询,对带有GROUPBY、ORDERBY等操作的优化。

所以,针对SPJ和非SPJ的查询优化,其实是对以上多种操作的优化。

“选择”和“投影”操作,可以在关系代数规则的指导下进行优化。

表连接,需要多表连接的相关算法完成优化。其他操作的优化多是基于索引和代价估算完成的。————物理优化。

逻辑查询优化包括的技术:

1子查询优化

2视图重写

3等价谓词重写

4条件化简

5外连接消除

6嵌套连接消除

7连接消除

8语义优化

9非SPJ的优化

Query Execution Plan of MySQL

语法格式:

EXPLAIN[explain_type] explainable_stmt

可选项包括:

EXTENDED|PARTITIONS|FORMAT = format_name

format_name:

  TRADITIONAL|JSON

说明:

1 EXPLAIN命令,显示SQL语句的查询执行计划。

2 EXPLAIN EXTENDED命令,显示SQL语句的详细的查询执行计划;之后可以通过“SHOW WARNINGS”命令查看详细的信息。

3 EXPLAIN PARTITIONS命令。显示SQL语句的带有分区表信息的查询执行计划。

4 EXPLAIN命令的输出格式有两种。

 4.1 TRADITIONAL;传统类型;按行隔离,每个标识一个子操作

 4.2 JSOn;JSON格式。

5 explainable_stmt,可被EXPLAIN执行的SQL语句,包括的类型有:

SELECT、INSERT、UPDATE、DELETE。

执行顺序

执行五表连接的查询语句如下:

代码语言:javascript
复制
EXPLAIN
SELECT *
FROM(t1 LEFT JOIN t2 ON true),(t3 FULL JOIN t4 ON true),t5
WHERE id1=id2 AND
      id2=id3 AND
 id3=id4 AND
 id4=id5;
008.png
008.png

结点解析

1) id:每个被独立执行的操作的标识,表示对象被操作的顺序;id值大,先被执行;如果相同,执行顺序从上到下。

2) select_type:查询中每个select子句的类型;

3) table:名字,被操作对象的名称,通常是表名,但有其他格式。

4) partitions:匹配的分区信息(对于非分区表值为NULL)。

5) type:连接操作的类型;

6) possible_keys:备选的索引(列出可能被使用到的索引)

7) key:经优化器选定的索引;常用“ANALYZE TABLE”命令可以使优化器正确的选择索引。

8) key_len:被优化器选定的索引键的长度,单位是字节。

9) ref:表示本行被操作的对象的参照对象(被参照的对象可能是一个常用量“const”表示,也可能是其他的key指向的对象)。

10) rows:查询执行所扫描的元组个数(对于InnoDB,此值是个估计值)。

11) filtered:按照条件表上数据被过滤的元组个数的百分比,“rows X filtered/100”可求出过滤后的元组数即实际的元组数。

子查询的优化

当一个查询是另一个查询的子部分时,称之为子查询(查询语句中嵌套有查询语句)

查询的子部分,包括哪些情况:

1目标列位置。

子查询如果位于目标列,则只能是标量子查询,否则数据库可能返回类似“错误:子查询必须只能返回一个字段”的提示。

示例:

代码语言:javascript
复制
CREATE TABLE t1(k1 INT PRIMARY KEY,c1 INT);

CREATE TABLE t2(k2 INT PRIMARY KEY,c2 INT);
INSERT INTO t2 VALUES(1,10),(2,2),(3,30);
012.png
012.png
011.png
011.png
010.png
010.png
009.png
009.png

2 FORM字句位置

相关子查询出现在FROM子句中,数据库可能返回类似“在FROM子句中的子查询无法参考相同查询级别中的关系”的提示,所以相关子查询不能出现在FROM子句中;

非相关子查询出现在FROM子句中,可上拉子查询到父层,在多表连接时统一考虑连接代价然后择优。

示例:

014.png
014.png
013.png
013.png

3 WHERE子句位置

出现在WHERE子句中的子查询,是一个条件表达式的一部分,而表达式可以分解为操作符和操作数;根据参与运算的不同的数据类型,操作符也不尽相同,如INT类型有“<、>、=、<>”等操作,这对子查询均有一定的要求(如INT型的等值操作,要求查询必须是标量子查询)。另外,子查询出现在WHERE子句中的格式,也有用谓词指定的一些操作,如IN、BETWEEN、EXISTS等。

示例:

016.png
016.png
015.png
015.png

4 JOIN/ON子句位置

JOIN/ON子句可以拆分为两部分,一是JOIN块类似于FROM子句,二是ON子句块类似于WHERE子句,这两部分都可以出现子查询。子查询的处理方式同FROM子句和WHERE子句。

5 GROUPBY子句位置

目标列必须和GROUPBY关联.可将子查询写在GROUPBY位置处,但子查询用在GROUPBY处没有实用意义。

6ORDERBY子句位置

可将子查询写在ORDERBY位置处,但ORDERBY操作是作用在整条SQL语句上的,子查询用在ORDERBY处没有实用意义。

子查询的类型——从对象间的关系看:

1 相关子查询

子查询的执行依赖于外层父查询的一些属性值。子查询因依赖于父查询的参数,当父查询的参数改变时,子查询需要根据新参数值重新执行(查询优化器对相关子查询进行优化有一定意义),如:

017.png
017.png

2 非相关子查询

子查询的执行,不依赖于外层父查询的任何属性值。这样子查询具有独立性,可独自求解,形成一个子查询计划先于外层的查询求解,如:

018.png
018.png

子查询的类型——从特定谓词来看:

1 [NOT]IN/ALL/ANY/SOME子查询

语义相近,表示“[取反]存在/所有/任何/任何”,左面是操作数,右面是子查询,是最常见的子查询类型之一。

2 [NOT]EXISTS子查询

半连接语义,表示“[取反]存在”,没有左操作数,右面是子查询,也是最常见的子查询类型之一。

3其他子查询

除了上述两种外的所有子查询。

子查询的类型——从语句的构成复杂程度来看:

1 SPJ子查询

有选择、连接、投影操作组成的查询

2 GROUPBY子查询

SPJ子查询加上分组、聚集操作组成的查询。

3其他子查询

GROUPBY子查询中加上其他子句如Top-N、LIMIT/OFFSET、集合、排序等操作。

后两中子查询有时合称非SPJ查询。

子查询的类型——从结果的角度来看

1 标量子查询

子查询返回的结果集类型是一个简单值(return a scalar, a single value)。

2单行单列子查询

子查询返回的结果集类型是零条或一条单元组(return a zero or single row, but only a column).相似于标量子查询,但可能返回零条元组。

3 多行单列子查询

子查询返回的结果集类型是多条元组但只有一个简单列(return multiple rows, but only a column)。

4 表子查询

子查询返回的结果集类型是一个表(多行多列)(return a table, one or more rows of one or more columns)。

为什么要做子查询优化?

在数据库实现早期,查询优化器对子查询一般采用嵌套执行的方式,即父查询中的每一行,都执行一次子查询,这样子查询会执行很多次。这种执行方式效率低。

而对子查询进行优化,可能带来几个数量级的查询效率的提高。子查询转变成为连接操作之后,会得到如下好处:

1子查询不用执行很多次。

2优化器可以根据统计信息来选择不同的连接方法和不同的连接顺序。

子查询中的连接条件、过滤条件分别变成了父查询的连接条件、过滤条件,优化器可以对这些条件进行下推,以提高执行效率。

How to optimize SubQuery?

1 子查询合并(SubQuery Coalescing)

在某些条件下(语义等价:两个查询块产生同样的结果集),多个子查询能够合并成一个子查询(合并后还是子查询,以后可以通过其他技术消除掉子查询)。这样可以把多次表扫描、多次连接减少为单次表扫描和单次连接,如:

SELECT * FROM t1 WHERE a1<10 AND(

EXISTS(SELECT a2 FROM t2 WHERE t2.a2<5 AND t2.b2=1)OR

EXISTS(SELECT a2 FROM t2 WHERE t2.a2<5 AND t2.b2=2)

);

可优化为:

SELECT * FROM t1 WHERE a1<10 AND(

EXISTS(SELECT a2 FROM t2 WHERE t2.a2<5 AND (t2.b2=1 OR t2.b2=2))

        /*两个ESISTS子句合并为一个,条件也进行了合并*/

);

2 子查询展开(SubQuery Unnesting)

又称为子查询反嵌套,又称为子查询上拉。

把一些子查询置于外层的父查询中,作为连接关系与外层父查询并列,其实质是把某些子查询重写为等价的多表连接操作(展开后,子查询不存在了,外部查询变成了多表连接)。

带来的好处是,有关的访问路径、连接方法和连接顺序可能被有效使用,使得查询语句的层次尽可能地减少。

常见的IN/ANY/SOME/ALL/EXISTS依据情况准换为半连接(SEMI JOIN)、普通类型的子查询消除等情况属于此类,如:

SELECT * FROM t1,(SELECT * FROM t2 WHERE t2.a2>10) v_t2

WHERE t1.a1<10 AND v_t2.a2<20;

可优化为:

SELECT * FROM t1,t2 WHERE t1.a1<10 AND t2.a2<20 AND t2.a2>10;

/*子查询变为了t1、t2表的连接操作,相当于把t2表从子查询中上拉了一层*/

3 聚集子查询消除(Aggregate SubQuery Elimination)

通常,一些系统支持的是标量聚集子查询消除。

如:

SELECT * FROM t1 WHERE t1.a1>(SELECT avg(t2.a2) FROM t2);

MySQL可以优化什么格式的子查询?

MySQl支持对简单SELECT查询中的子查询优化,包括:

1 简单SELECT查询中的子查询。

2 带有DISTINCT、ORDERBY、LIMIT操作的简单SELECT查询中的子查询。

CREATE TABLE t1 (a1 INT, b1 INT, PRIMARY KEY (a1));

CREATE TABLE t2 (a2 INT, b2 INT, PRIMARY KEY (a3));

CREATE TABLE t3 (a3 INT, b3 INT, PRIMARY KEY (a3));

插入10000行与上例同样的数据。

查询执行计划如下:

mysql>EXPLAIN EXTENDED SELECT * FROM t1 WHERE t1.a1<100 AND a1 IN (SELECT a2 FROM t2 WHERE t2.a2>10);

019.jpg
019.jpg

MySQL不支持对如下情况的子查询进行优化:

带有UNION操作。

带有GROUPBY、HAVING、聚集函数。

使用ORDERBY中带有LIMIT。

内表、外表的个数超过MySQL支持的最大表的连接数。

聚集函数操作在子查询中,查询执行计划如下:

020.jpg
020.jpg

子查询合并技术,不支持:

mysql>explain extended select * from t1 where a1<4 and (exists (select a2 from t2 where t2.a2<5 and t2.b2=1) or exists(select a2 from t2 where t2.a2<5 and t2.b2=2));

022.jpg
022.jpg
021.jpg
021.jpg

子查询展开(子查询反嵌套)技术,支持的不够好

mysql>explain extended select * from t1,(select * from t2 where t2.a2>10)v_t2 where t1.a1<10 and v_t2 .a2<20;

023.jpg
023.jpg

再看一个IN子查询的例子,查询执行计划如下:

mysql>explain extended select * from t1 where ta.a1<100 and a1 in (select a2 from t2 where t2.a2>10);

024.jpg
024.jpg
025.jpg
025.jpg

……

聚集子查询消除技术,不支持

mysql>explain extended select * from t1 where t1.a1>(select min(t2.a2) from t2);

026.jpg
026.jpg

Q:MySQL为什么不支持聚集子查询消除?

A:1 MySQL认为,聚集子查询,只需要执行一次,得到结果后,即可把结果缓冲到内存中供后续连接或过滤等操作使用,没有必要消除子查询。

2另外,如果聚集子查询在索引列上执行,则会更快得到查询结果,更能加速查询速度。

027.jpg
027.jpg

MySQL支持对哪些类型的子查询进行优化?

示例1 MySQL不支持对EXISTS类型的子查询做近一步的优化。

被查询优化器处理后的语句为:

028.jpg
028.jpg

EXISTS类型的相关子查询,查询执行计划如下:

mysql>explain extended select* from t1 where exists (select 1 from t2 where t1.a1=t2.a2 and t2.a2>10);

029.jpg
029.jpg

示例2 MySQL不支持对NOT EXISTS类型的子查询做进一步的优化。

被查询优化器处理后的语句为:

030.jpg
030.jpg

NOT EXISTS类型的相关子查询的查询执行计划如下:

mysql>explain extended select * from t1 where NOT EXISTS (select 1 from t2 where t1.a1=t2.a2 and t2.a2>10);

031.jpg
031.jpg

示例3 MySQL支持对IN类型的子查询的优化。

IN非相关子查询,查询计划如下:

mysql>explain extended select * from t1 where t1.a1 IN (select a2 from t2 where t2.a2>10);

032.jpg
032.jpg

被查询优化器处理后的语句为

033.jpg
033.jpg

IN相关子查询,查询执行计划如下:

mysql>explain extended select * from t1 where t1.a1 IN(select a2 from t2 where t1.a1=10);

034.jpg
034.jpg

被查询优化器处理后的语句为:

035.jpg
035.jpg

示例4 MySQL支持对NOT IN类型的子查询的优化。

NOT IN非相关子查询,查询计划如下:

mysql>explain extended select * from t1 where t1.a1 NOT IN (select a2 from t2 where t2.a2>10);

036.jpg
036.jpg

被查询优化器处理后的语句为

037.jpg
037.jpg

示例5 MySQL支持对ALL类型的子查询的优化。

ALL非相关子查询,查询计划如下:

mysql>explain extended select * from t1 where t1.a1 >ALL (select a2 from t2 where t2.a2>10);

038.jpg
038.jpg

被查询优化器处理后的语句为

039.jpg
039.jpg

mysql>explain extended select * from t1 where t1.a1 =ALL (select a2 from t2 where t2.a2=10);

040.jpg
040.jpg

被查询优化器处理后的语句为

041.jpg
041.jpg

mysql>explain extended select * from t1 where t1.a1 <ALL (select a2 from t2 where t2.a2=10);

042.jpg
042.jpg

被查询优化器处理后的语句为

043.jpg
043.jpg

示例6 MySQL支持对SOME类型的子查询的优化。

使用了“>SOME”式子的子查询被优化,查询计划如下:

mysql>explain extended select * from t1 where t1.a1 >SOME (select a2 from t2 where t2.a2>10);

044.jpg
044.jpg

被查询优化器处理后的语句为

045.jpg
045.jpg

使用了“=SOME”式子的子查询被优化,查询计划如下:

mysql>explain extended select * from t1 where t1.a1 =SOME (select a2 from t2 where t2.a2=10);

046.jpg
046.jpg

被查询优化器处理后的语句为

047.jpg
047.jpg

使用了“<SOME”式子的子查询被优化,查询计划如下:

mysql>explain extended select * from t1 where t1.a1 <SOME (select a2 from t2 where t2.a2=10);

048.jpg
048.jpg

被查询优化器处理后的语句为

049.jpg
049.jpg

示例7 MySQL支持对ANY类型的子查询的优化。

050.jpg
050.jpg

使用了“=ANY”式子的子查询被优化,查询计划如下:

mysql>explain extended select * from t1 where t1.a1 =ANY (select a2 from t2 where t2.a2>10);

051.jpg
051.jpg

被查询优化器处理后的语句为

052.jpg
052.jpg

使用了“<ANY”式子的子查询被优化,查询计划如下:

mysql>explain extended select * from t1 where t1.a1 <ANY (select a2 from t2 where t2.a2>10);

053.jpg
053.jpg

被查询优化器处理后的语句为

054.jpg
054.jpg
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018-11-21 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 数据库查询优化技术总览
  • 查询的基本操作
  • 查询的2种类型
  • Query Execution Plan of MySQL
  • 子查询的优化
  • How to optimize SubQuery?
  • MySQL可以优化什么格式的子查询?
相关产品与服务
云数据库 SQL Server
腾讯云数据库 SQL Server (TencentDB for SQL Server)是业界最常用的商用数据库之一,对基于 Windows 架构的应用程序具有完美的支持。TencentDB for SQL Server 拥有微软正版授权,可持续为用户提供最新的功能,避免未授权使用软件的风险。具有即开即用、稳定可靠、安全运行、弹性扩缩等特点。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档