文章目录
1 子查询
子查询又称嵌套查询
一般使用的 select 语句都是简单查询,是单条 select 语句。
子查询中有多条 select 语句,即在单条 select 语句中嵌套一条或多条 select 语句。如,将一条 select 语句的查询结果作为另一条 select 语句的查询条件。如,将一条 select 语句的查询结果作为另一条 select 语句的计算字段。
注意: 子查询是目前明确可知的 SQL 运行效率最低的一种查询方式,在有多种查询方案时尽可能不使用子查询。
2 关系表
SQL最强⼤的功能之⼀就是能在数据检索查询的执⾏中联结(join)表。
在能够有效地使⽤联结前,必须了解关系表以及关系数据库设计的⼀些基础知识。
2.1 表关系
表之间的关系通过外键来确定。
外键: 表1中的一个字段在另一张表2中也存在,且是主键,则该字段就是表1的外键。
外键实现方式有两种:物理外键,逻辑外键。
物理外键: 在创建表时指定字段为外键,强关联到某张表中的某个字段,需要使用 SQL 语句来实现。
逻辑外键: 创建表时定义普通的字段,没有强关联关系,仅在使用时通过程序逻辑来实现。
开发中推荐使用逻辑外键,不推荐使用逻辑外键,原因:
- 物理外键使用强关联,需要特殊的 SQL 语句,比较复杂。
- 强关联的表在使用时可能遇到问题
- 删除的记录如果存在正在使用的外键时是无法删除的。
- 修改字段时,由于强关联的存在,在并发时会影响程序的执行效率。
表关系即表与表之间的关系:一对一,一对多,多对多。
一对一: 表中的一条数据对应另一张表中的一条数据,如学生的信息表与详情信息表,一条学生信息对应一条学生详情信息。
一对多: 表中的一条数据对应另一张表中的多条数据(反过来既是多对一),如班级表与学生表,一个班级对应多个学生。
多对多: 表中的多条数据对应另一张表中的多条数据,如课程表与学生表,一个课程有多个学生,一个学生可选多个课程。
2.2 表联结
使用一条 select 语句查询存在于多张表的数据时可以使用表联结。
表联结是一种机制,用来在一条 select 语句中关联表。
表联结有两种方式:where 和 inner join。
举例:
学生表 stu:学生编号 stu_id、学生姓名 stu_name、班级编号 class_id
班级表 class:班级编号 class_id、班级名称 class_name
查询所有学生的学生编号、学生姓名、班级名称。
使用 where 进行表联结:
select stu_id,stu_name,class_name from stu,class where stu.class_id=class.class_id
使用 where 表联结时一定不能忘了 where 子句,否则返回的将是联结表的笛卡尔积,结果行数是所有表的行数之积。
使用 inner join 进行表联结:
select stu_id,stu_name,class_name from stu inner join class on stu.class_id=class.class_id
同 where 表联结类似,使用 inner join 时不能忘了 on 后的联结条件,否则会返回笛卡尔积。
表联结有三种:内部联结、自联结、外部联结。
内部联结: where 和 inner join(也可以使用 join ,不过 SQL 规范首选 inner join)。
自联结: inner join ,使用一张表联结自身进行查询。
外部联结: 左联结 left join ,右联结 right join 。
左联结 left join:以左侧的表为基准,关联右侧的表进行联结,未关联的数据用 null 表示。
右联结 right join:以右侧的表为基准,关联左侧的表进行联结,未关联的数据用 null 表示。
3 组合查询 union
MySQL也允许执⾏多个查询(多条SELECT语句),并将结果作为单个查询结果集返回。
这些组合查询通常称为并(union)或复合查询(compound query)。
UNION规则
- UNION必须由两条或两条以上的SELECT语句组成,语句之间⽤关键字UNION分隔(因此, 如果组合4条SELECT语句,将要使⽤3个UNION关键字)。
- UNION中的每个查询必须包含相同的列、表达式或聚集函数(不过各个列不需要以相同的次 序列出)
- 列数据类型必须兼容:类型不必完全相同,但必须是DBMS可以隐含地转换的类型(例如, 不同的数值类型或不同的⽇期类型)。
组合查询方式:
select 语句1
union
select 语句2;
注意:
- 组合查询会执行所有 select 语句并将所得的结果集组合成一个结果集返回。
- 组合查询默认会去除结果集中重复的行,如果不想去重则将 union 替换为 union all 即可。
- 组合查询只允许存在一条 order by 排序子句,且它必须是在最后一条 select 语句之后。
4 sql_mode
sql_mode 是 MySQL 数据库中的一个环境变量,它定义了 MySQL 支持的 SQL 语法和数据校验等。
查看当前数据库的sql_mode
select @@sql_mode;
sql_mode默认值为 ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
更改MySQL数据库的sql_mode:
1.临时修改,MySQL服务重启后失效
set @@sql_mode = '改后的值';
2.修改MySQL的配置文件,MySQL服务重启后永久生效
打开MySQL的配置文件my.cnf
找到[mysqld]
在下方添加 sql_mode = '改后的值';
sql_mode值的含义:

案例:
按多个字段进行分组并计算每组的行数: select 字段名1,字段名2,count(*) from 表名 group by 字段名1,字段名2;
针对上条语句,如果仅对字段1进行分组,字段2只是要查询的字段,可能会有下述写法:
select 字段名1,字段名2,count(*) from 表名 group by 字段名1; 注意:该种写法在默认情况下会报错。
原因:默认情况下 sql_mode 中存在 ONLY_FULL_GROUP_BY
ONLY_FULL_GROUP_BY 针对 group by 聚合操作,如果在 select 中的列,没有在 group by 中出现,那么将认为 sql 不合法。
解决方案:
临时修改 sql_mode 去除 ONLY_FULL_GROUP_BY,则上述语句合法。
修改配置文件去除 ONLY_FULL_GROUP_BY,则上述语句合法
使用函数 any_value(filed),允许返回非分组字段,效果等同于关闭 ONLY_FULL_GROUP_BY 模式,对上述语句修改如下:
select 字段名1,any_value(字段名2),count(*) from 表名 group by 字段名1;
建议:默认的 sql_mode 符合 SQL 标准,尽量不要修改,有需要推荐使用解决方案3.
5 MySQL事务
事务(Transaction)是由⼀系列对系统中数据进行访问与更新的操作所组成的⼀个程序执行逻辑单元。

5.1 事务的语法
start transaction; --开始事务,也可以使用 begin;
sql语句1;
sql语句2;
...
commit; --提交事务,确认此次修改
begin; --开始事务
sql语句1;
sql语句2;
...
rollback; --回滚事务,放弃此次修改
5.2 事务的ACID特性
1.原子性(Atomicity)
事务的原子性是指事务是一个原子的操作单元,在一次执行过程中事务的所有操作要么全部执行成功,要么全部执行失败。事务开始后所有操作,要么全部做完,要么全部不做,不可能停滞在中间环节。事务执行过程中出错,会回滚到事务开始前的状态,所有的操作就像没有发生⼀样。也就是说事务是⼀个不可分割的整体,就像化学中学过的原子,是物质构成的基本单位。
2.一致性(Consistency)
事务的一致性是指事务在执行前和执行后都不会破坏数据库数据的完整性和一致性。如账户 A 和账户 B 共有 2000元,无论账户 A 和 B 之间如何转账,事务结束后,账户 A 和账户 B 仍共有 2000元。
3.隔离性(Isolation)
事务的隔离性是指在并发环境中,并发的多个事务是相互隔离的,不同的事务操作相同的数据时,每个事务都有各自完整的数据空间,一个事务内部的操作和使用的数据对其他并发事务是隔离的。隔离级别有 4 个,参考下述事务的隔离级别。
4.持久性(Duration)
事务的持久性是指事务一旦提交后,事务对数据库的更改就是永久性的,不论服务器系统发生何种故障,只要数据库重新启动后就会将其恢复到事务成功结束后的状态。
5.3 事务的并发问题
事务在并发过程中可能遇到以下问题:
1.脏读
事务 A 读取了事务 B 更新的数据,然后 B 回滚操作,那么 A 读到的数据就是脏数据。
2.不可重复读
事务 A 多次读取同⼀数据,事务 B 在事务 A 多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同⼀数据时,结果不⼀致。
3.幻读
重复查询的过程中,数据就发生了量的变化(增加或减少)。
注意: 不可重复读和幻读容易混淆,不可重复读侧重于修改,幻读侧重于增加或减少。
5.4 事务的隔离级别
事务共有 4 种隔离级别,不同的隔离级别可能导致不同的并发问题。
| 事务的隔离级别 | 脏读 | 不可重复读 | 幻读 |
|---|---|---|---|
| 读未提交(READ UNCOMMITTED) | 允许 | 允许 | 允许 |
| 读已提交(READ COMMITTED) | 禁止 | 允许 | 允许 |
| 可重复读(REPEATABLE READ) | 禁止 | 禁止 | 可能会 |
| 序列化(SERIALIZABLE) | 禁止 | 禁止 | 禁止 |
从上到下,级别越高,安全性越高,并发性越低。MySQL 默认隔离级别是可重复读,Oracle 和 SQLserver 默认隔离级别是读已提交。
查看当前会话的事务隔离级别:
select @@tx_isolation;
设置当前会话的事务隔离级别:
set session transaction isolation level XXX;
1.读未提交(READ UNCOMMITTED)
一个事务在对某行数据进行修改时,另一个事务将无法对该行数据进行删改操作,但是可以读取该行数据。即事务 A 读取了事务 B 未提交的数据,若事务 B 回滚,则事务 A 读到的就是脏数据。
案例:
| 时间 | 事务 A | 事务 B |
|---|---|---|
| T1 | 开始事务 | |
| T2 | 开始事务 | |
| T3 | 读取余额1000元 | |
| T4 | 取出500元,则余额500元 | |
| T5 | 读取余额500元(脏读) | |
| T6 | 撤销事务,则余额1000元 | |
| T7 | 结束事务 |
2.读已提交(READ COMMITTED)
一个事务 A 在对某行数据进行修改时,另一个事务 B 将无法对该行数据进行删改操作,也无法读到事务 A 修改后的数据,只能读到事务 A 修改前的该行数据,只有当事务 A 提交后,事务 B 才能读到事务 A 修改后的该行的最新数据。即事务 B 在事务 A 提交前后的两次读取的数据可能不一样导致不可重复读问题。
案例:
| 时间 | 事务 A | 事务 B |
|---|---|---|
| T1 | 开始事务 | |
| T2 | 开始事务 | |
| T3 | 读取余额1000元 | 读取余额1000元 |
| T4 | 取出500元,则余额500元 | |
| T5 | 读取余额1000元 | |
| T6 | 提交事务 | |
| T7 | 读取余额500元(不可重复读) | |
| T8 | 提交事务 |
3.可重复读(REPEATABLE READ)
一个事务 A 读取了一行数据,此时无论其他事务对该行数据做出任何删除或修改,且无论事务提交或回滚,事务 A 的整个过程中,该行数据会一直存在且不会变。当事务 A 执行了一句查询 select 语句后,事务 B 向表中插入新的数据并提交事务后,事务 A 进行了一次表范围的修改后,若事务 A 再次执行相同的查询 select 语句就会发现查询结果和预期的不同,产生了量的变化,这就是幻读。
| 时间 | 事务 A | 事务 B |
|---|---|---|
| T1 | 开始事务 | |
| T2 | 开始事务 | |
| T3 | select 语句查询数据,得到 X 条记录 | |
| T4 | 插入一条数据 | |
| T5 | 提交事务 | |
| T6 | 进行表范围的数据修改 | |
| T7 | 相同 select 语句查询数据,得到 X + 1 条记录(幻读) | |
| T8 | 提交事务 |
4.序列化(SERIALIZABLE)
最严格的事务隔离级别,所有事务只能排队顺序执行,不能并发。
5.5 不同事务隔离级别的锁
- 读未提交(RU):有⾏级的锁,没有间隙锁。它与RC的区别是能够查询到未提交的数据。
- 读已提交(RC):有⾏级的锁,没有间隙锁,读不到没有提交的数据。
- 可重复读(RR):有⾏级的锁,也有间隙锁,每次读取的数据都是⼀样的,两个锁组合可以解决幻读的问题。
- 序列化(S):有⾏级锁,也有间隙锁,读表的时候,就已经上锁了。
5.6 事务的隐式提交
SQL 语句分为四种:
- DQL:查询语句。
- DML:写操作,增删改。
- DDL:定义语句,建库,建表,修改表,索引操作,存储过程,视图。
- DCL:用户授权,删除授权。
所有的 DDL 类型的语句都会导致隐式提交,即执行 DDL 语句时会默认先执行 commit ,然后执行 DDL 。
6 存储过程
存储过程:为了完成某特定功能的由一条或多条 SQL 语句组成的集合,经过编译后存储在数据库中,需要时直接调用即可。
存储过程的优点:
- 执行速度快,存储过程在创建时就进行了编译,使用时可以直接调用,而普通的 SQL 语句每次执行都要编译。
- 减少网络通信量,有多条 SQL 语句的复杂操作时只需要连接数据库一次使用存储过程即可,否则需要多次连接数据库获取数据然后进行业务逻辑处理。
- 安全性高,使用存储过程比执行多条 SQL 语句稳定,还可以分配不同的用户使用权。
- 可维护性高,需要更改业务逻辑时只需要修改存储过程即可,不需要重启服务器,也不需要修改,测试,部署应用程序。
存储过程的缺点:
- 开发调试差,存储过程比较复杂,且没有比较好的开发工具,不便于开发和调试。
- 可移植性差,存储过程都在数据库里,使用存储过程来实现业务逻辑会限制应用程序的可移植性。
- SQL 本身是结构化查询语言,擅长的是数据查询,处理复杂的业务逻辑会比较吃力。
创建存储过程:
delimiter //
create procedure p1()
begin
sql语句1;
sql语句2;
sql语句3;
...
end
//
delimiter ;
-- delimiter用于设置结束命令的符号,默认是分号 ; ,由于存储过程语句过长需要用分号分隔语句,而默认分号表示命令结束可以执行了
-- 这里修改结束符号为 // ,最后使用//表示命令结束可以执行,则存储过程就创建好了,最终将符号改回分号。
-- 对于p1(),其中p1为存储过程的名字,括号内可以放参数,类似函数和方法的定义。
-- 存储过程中的SQL语句集必须在begin和end间。
执行存储过程:
call p1();
查看存储过程:
show create procedure p1\G
删除存储过程:
drop procedure p1;
7 触发器
触发器是MySQL响应写操作(增、删、改)⽽⾃动执⾏的⼀条或⼀组定义在BEGIN和END之间的 MySQL语句
或可理解为:提前定义好⼀个或⼀组操作,在指定的SQL操作前或后来触发指定的SQL⾃动执⾏
触发器就像是JavaScript中的事件⼀样
触发器语法:
CREATE TRIGGER trigger_name trigger_time trigger_event ON tbl_name FOR EACH ROW trigger_stmt
说明:
# trigger_name:触发器名称
# trigger_time:触发时间,可取值:BEFORE或AFTER
# trigger_event:触发事件,可取值:INSERT、UPDATE或DELETE。
# tb1_name:指定在哪个表上
# trigger_stmt:触发处理SQL语句。
-- 查看所有的 触发器
show triggers\G;
-- 删除触发器
drop trigger trigger_name;
触发器案例:
-- 创建⼀个删除的触发器,在users表中删除数据之前,往del_users表中添加⼀个数据
-- 1,复制当前的⼀个表结构
create table del_users like users;
-- 2,创建 删除触发器 注意在创建删除触发器时,只能在删除之前才能获取到old(之前的)数据
\d //
create trigger deluser before delete on users for each row
begin
insert into del_users values(old.id,old.name,old.age,old.account);
end;
//
\d ;
-- 3 删除users表中的数据去实验,\d效果等同于delimiter
注意:
在 INSERT 触发器代码内,可引⽤⼀个名为 NEW 的虚拟表,访问被插⼊的⾏
在 DELETE 触发器代码内,可以引⽤⼀个名为 OLD 的虚拟表,访问被删除的⾏
OLD 中的值全都是只读的,不能更新
在 AFTER DELETE 的触发器中⽆法获取 OLD 虚拟表
在 UPDATE 触发器代码中
- 可以引⽤⼀个名为 OLD 的虚拟表访问更新以前的值
- 可以引⽤⼀个名为 NEW 的虚拟表访问新更新的值
8 视图
什么是视图:
- 视图是虚拟的表。与包含数据的表不⼀样,视图只包含使⽤时动态检索数据的查询。
- 视图仅仅是⽤来查看存储在别处的数据的⼀种设施或⽅法。
- 视图本身不包含数据,因此它们返回的数据是从其他表中检索出来的。
- 在添加或更改这些表中的数据时,视图将返回改变过的数据。
- 因为视图不包含数据,所以每次使⽤视图时,都必须处理查询执⾏时所需的任⼀个检索。
- 如果你⽤多个联结和过滤创建了复杂的视图或者嵌套了视图,可能会发现性能下降得很厉害。
视图的作用:
- 重⽤SQL语句。
- 简化复杂的SQL操作。在编写查询后,可以⽅便地重⽤它⽽不必知道它的基本查询细节。
- 使⽤表的组成部分⽽不是整个表。
- 保护数据。可以给⽤户授予表的特定部分的访问权限⽽不是整个表的访问权限。
- 更改数据格式和表示。视图可返回与底层表的表示和格式不同的数据。
- 注意:视图不能索引,也不能有关联的触发器或默认值。
视图的语法:
创建视图:
create view 视图名 as select语句;
删除视图:
drop view 视图名;
修改视图:
alter view 视图名 as select语句;
查看当前库中的所有表和视图:
show tables;
查看当前库中的所有视图:
show table status where comment='view';
9 MySQL索引
9.1 索引的概念
什么是索引?
索引类似图书的目录索引,可以提高数据检索的效率,降低数据库的 IO 成本。
官方定义:索引是帮助 MySQL 高效获取数据的数据结构。
可理解为快速查找排好序的一种数据结构。
9.2 索引的分类
索引主要分为五类:主键索引、唯一索引、普通索引、组合索引、全文索引。
索引的效率从左到右依次递减,其中普通索引和组合索引的效率差不多。
主键索引
根据主键建立的索引既是主键索引,该索引效率最高,不允许空值,不允许重复。
如果在建表时没有定义主键,则 innodb 表引擎会使用非空唯一索引作为主键代替。
如果既没有主键也没有非空唯一索引,则 innodb 表引擎会隐式的创建一个主键来作为索引,虽然该索引无法使用,但一定会存在。

唯一索引
用来建立索引的列的值必须是唯一的,允许空值(也允许多个空值)。

普通索引
用表中的普通列建立的索引,没有任何限制。

组合索引
由多个列组合建立的索引,多个列中的值不允许空值。

组合索引的使用遵循 “最左原则”
假设索引依照 index(a,b,c) 的方式建立,只有当查询语句的 where 条件中使用了 a 、a 和 b 、a 和 b 和 c 这三种情况,组合索引才会生效,其他的检索条件均不会用到组合索引。
全文索引
使用大文本对象的列建立的索引。

5.6 之前的版本中,全⽂索引只能⽤于 MyISAM 存储引擎,5.6 及以后的版本,MyISAM 和 InnoDB 均⽀持全⽂索引。
在之前的 MySQL 中,全⽂索引只对英⽂有⽤,对中⽂还不⽀持,MySQL8 的版本中⽀持了对中⽂分词的全⽂索引。
9.3 索引的实现原理
索引的实现有两种:哈希索引,B+TREE 索引。
只有 memory 存储引擎支持哈希索引,哈希索引根据索引列的值计算出 hashcode,然后在 hashcode 相应的位置存储该值对应列的所在行数据的物理位置。因为这种散列算法所以哈希索引的访问速度非常快,但是一个值只能对应一个 hashcode,所以哈希索引不支持范围查找,也不支持排序功能。
一般在不指定索引类型的情况下,存储引擎都使用 B+TREE 索引,B+TREE 索引由 B TREE 索引演变而来。
B TREE 索引

以上图例为一个 3 阶 B 树,它有以下特点:
- 查找效率不均衡。
- 范围查找需要中序遍历。
- 每一个节点上都存有数据。
B+TREE 索引

以上图例为一个 3 阶 B+ 树,它有以下特点:
- 只有叶子节点存有数据。
- 非叶子结点起到索引作用。
- 所有叶子节点使用链表相连。
B 树和 B+ 树对比:
磁盘读写代价更低
计算机中所有空间相关操作都是按照块(block)的方式进行存取和操作的。B 树中,数据和索引都在一个节点上,所以一个块能读取到的索引就更少,如果要读取更深层的数据则需要读取更多的块,即更多的 IO 次数。B+ 树相比 B 树,一个块能读取更多的索引,即使要读取更深层的数据也只需要读取少量的块,即更少的 IO 次数。
随机 IO 的次数更少
随机 IO 指读写操作时间连续,但是访问地址不连续。顺序 IO 指读写操作基于逻辑块逐个访问相邻地址的数据,相比于随机 IO 时间要短。相同情况下,B 树进行更多的随机 IO,B+ 树进行更多的顺序 IO,因此 B+ 树的随机 IO 更少,效率更高。
查询速度更稳定
B 树的所有节点都存有数据,所以对于不同数据它的查询速度也不一样。B+ 树只有叶子节点存有数据,而叶子节点的高度又都是相同的,所以所有数据的查询速度都是一样的。
主索引和辅助索引
索引按照索引的键可以分为主索引和辅助索引,使用主键建立的索引是主索引,其他的都是辅助索引。主索引只能存在一个,辅助索引可以有多个。
聚簇索引和非聚簇索引
主索引可以视为聚簇索引,辅助索引可以视为非聚簇索引。

如上图就是一个聚簇索引,叶子节点将主键索引值与对应的行记录数据存在一起,找到索引则表示找到数据,所以使用主键建立的聚簇索引查找效率非常高。

如上图就是一个非聚簇索引,叶子节点存储的是索引值和 key,这个 key 就是索引值所在行记录的相应的聚簇索引键,根据找到的 key 值回表,即在聚簇索引中找到对应节点,则可以找到对应的行记录数据。
总结:使用非聚簇索引(辅助索引)查找数据需要回表依赖聚簇索引(主索引)才能找到整行数据。则聚簇索引必须存在且只能存在一个,辅助索引可以存在且能存在多个。并且非聚簇索引的查找效率低于聚簇索引。以上仅在 InnoDB 引擎下。
MyISAM 引擎下的索引

如上图 MyISAM 下的索引有同 InnoDB 一样的树形结构,但是在叶子节点中既没有行记录数据也没有聚簇索引键值 key,而是索引值和行号。因为 MyISAM 引擎会为索引单独建立一个文件(索引文件),根据索引可以找到一个行号,然后再数据文件中根据行号找到数据。不论是主索引还是辅助索引都是通过索引找到行号,然后回行,根据行号找到数据,所以 MyISAM 下的所有索引都是非聚簇索引,也称为二级索引。
InnoDB 和 MyISAM 的区别
| InnoDB | MyISAM | |
|---|---|---|
| 数据存储方式 | 由两个文件组成,表结构,数据和索引 | 由三个文件组成,表结构、数据、索引 |
| 索引方式 | 底层基于 B + Tree 数据结构建立,主索引为聚簇索引,辅助索引为非聚簇索引 | 底层基于 B + Tree 数据结构建立,所有索引都是非聚簇索引 |
| 事务支持 | 支持事务 | 不支持事务 |
10 MySQL慢查询与SQL优化
10.1 MySQL 慢查询
MySQL 的慢查询全称为慢查询日志,是 MySQL 提供的一种日志记录,用来记录响应时间超过阈值的语句。MySQL 默认不启动慢查询日志,若要启动需要手动配置参数。如没有调优需要尽量不要启动慢查询,它会造成一定的性能影响。
查看慢查询状态:show variables like '%slow_query_log%';
slow_query_log_file 的值为慢查询日志文件绝对路径。
开启慢查询日志:set global slow_query_log =on; 设置 off 即为关闭
查询触发慢查询的时间:show variables like 'long_query_time'; 触发时间默认为10秒
修改触发慢查询的时间:set long_query_time=XXX; XXX 为触发时间值,仅为数字,时间单位默认秒
注意: 在命令行中进行以上修改操作仅对当前数据库生效,一旦数据库重启则失效。若要永久生效需要在配置文件 my.ini 中设置或修改相关参数,并在服务器重启后永久生效。
10.2 MySQL 语句分析
一条查询语句经过 MySQL 查询优化器的各种基于成本和规则的优化后会生成一个执行计划,该计划展示了具体执行查询的方式,使用 explain 语句可以查看执行计划,这有助于 SQL 语句的分析和优化。
explain 使用方式: explain select语句 \G 在查询语句前加个 explain 即可,\G 为格式化显示
字段分析:
id 在⼀个⼤的查询语句中每个 SELECT 关键字都对应⼀个唯⼀的 id
select_type SELECT 关键字对应的那个查询的类型
table 表名
partitions 匹配的分区信息
type 针对单表的访问⽅法
possible_keys 可能⽤到的索引
key 实际上使⽤的索引
key_len 实际使⽤到的索引⻓度
ref 当使⽤索引列等值查询时,与索引列进⾏等值匹配的对象信息
rows 预估的需要读取的记录条数
Extra ⼀些额外的信息
10.3 SQL 优化
索引能极大的提高数据检索的效率,但是每次建立索引都会生成一个 B+ 树,且需要进行维护,这是很耗费性能和存储空间的,所以不能盲目的建立索引。需要适当的建立索引。
适当建立索引
- 创建并使用自增数字来建立主键索引
- 为经常作为 where 条件的字段建立索引
- 添加索引的字段尽可能的保持唯一性
- 使用组合索引而不是建立多个索引
- 尽可能的进行索引覆盖(将经常要查询的字段添加到组合索引中,则使用非聚簇索引可一次找到数据,不需要再次回表找其他数据)
索引是为了提高检索效率而创建的,因需要而创建的索引尽可能的要使用到,否则即影响性能也会浪费空间,而有些操作会导致索引不被使用,所以要合理使用索引。
合理使用索引
- 不在索引列上使用函数或进行运算
- 避免索引列发生隐式转换(where 条件中等号两端类型不一样可能导致隐式的类型转换)
- 使用 like 时避免将通配符放在开头
- 使用组合索引时注意最左原则
SQL 语句的优化也会提高查询效率。
SQL 语句优化
- 尽可能避免子查询(效率最低)
- 避免多表查询(将复杂查询简单化,多表拆为单表,单表建立索引)