MySQL高级
MySQL高级
一、distinct关键字
distinct能够去除查询结果中重复的记录
注意:数据表中的数据并没有被修改,实际上所有的DQL查询语句都不会修改原始数据,只是对查询结果进行处理
1 | # 准备工作 |
二、连接查询
连接查询的概念
针对一张数据表进行查询,称为单表查询。join关键字
将两张及以上的数据表联合起来进行查询,获取到多张数据表组合出来的数据,称为连接查询。
连接查询的分类
1 | 根据表连接的方式分类 |
笛卡尔积现象
一般是由于没有加任何限制条件直接联合多张表进行查询。查询结果的数量是多张表的乘积,但一般这样的查询没有太大意义,要避免这种情况需要设计好限制条件。
内连接
1 | # 准备数据 |
外连接
1 | # 内连接的多张表并没有主次关系,也就是说不需要考虑查询语句书写的先后顺序 |
更多表连接(三张表)
1 | # 三张及以上数据表连接查询,多个连接条件一般都与第一张表相关联,内外连接可以混合使用 |
三、子查询
子查询的概念
DQL语句中嵌套DQL语句,被嵌套的DQL语句称为子查询。
子查询的使用
子查询可写在select、from、where关键字后面。
1 | # where子句中的子查询,一般子查询结果是某个值 |
四、union关键字
union关键字能合并结果集,将多表查询的结果直接拼接起来,但多张表必须保持字段数一致。字段值在没有特殊逻辑关系下也要对应起来。
1 | # union和join |
五、limit关键字
limit的作用
将结果集的部分数据展示出来,常用于网页开发分页查询中
关键字执行优先级
执行优先级:from→where→group by→having→select→order by→limit
limit的使用
1 | # 格式:limit 开始下标(缺省的话,默认从0开始),长度(即数据条数) |
六、约束
约束的概念
约束(constraint)是用于保证数据完整性、有效性的一种规则。
多种约束规则
非空:not null
唯一:unique
主键:primary key(PK)
外键:foreign key(FK)
检查:check(MySQL不支持,Oracle支持)
约束的使用
1 | # 非空 not null |
七、存储引擎
存储引擎的概念
存储引擎是MySQL中特有的术语,表示一个数据表存储、组织数据的方式。不同的存储引擎,数据表存储数据的方式不同。
存储引擎的使用
1 | # 准备工作 |
常用存储引擎介绍
MyISAM存储引擎
使用三种文件来表示每个数据表:
- 格式文件(.frm):存储表结构的定义
- 数据文件(.myd):存储表行的内容
- 索引文件(.myi):存储表上索引(主键或unique约束字段会自动创建索引)
特点:可以被转换为压缩,只读表里节省空间,但不支持事务,安全性低
InnoDB存储引擎
一般开发中最优先考虑的存储引擎,重量级的存储引擎,管理的数据表有以下特征:
- 每个数据表在数据库目录中以.frm格式的文件表示
- 提供一组用于记录事务性获得的日志文件
- 通过commit、savepoint和rollback支持事务处理
- 提供全ACID兼容
- 在MySQL服务器崩溃后能够自动恢复
- 多版本MVCC和行级锁定
- 支持外键级引用的完整性,包括级联删除和更新
特点:支持事务,以保证数据的安全性,但效率不高,不能压缩不能转换为只读,比较占存储空间
MEMORY存储引擎
MEMORY存储引擎以前称为heap存储引擎,管理数据库有以下特征:
- 在数据库目录内,每个数据表均以.frm格式的文件表示
- 表的数据以及索引被存储在内存中(这显著提升了查询速度)
- 表级锁机制
- 不支持text和blob字段
特点:字段长度固定,数据存储在内存,这使得它的数据处理效率非常高,但同时一旦MySQL服务重启,存储数据就会丢失,一般开发中只用于临时存储数据。
八、事务、视图、数据库设计
事务(transaction)
事务的概念:一个事务就是一个完整的业务逻辑,是一个不可再分的最小的工作单元。例如,银行转账,A向B转账1000,A扣除1000,B增加1000。可以用两个update语句实现,但它们必须同时成功或者失败,而不能单独拆分,否则就不构成一个完整的业务逻辑,这就是一个最小的工作单元。
只有在使用DML语句时才会涉及到事务,因为只有DML语句会增加、删除、修改数据表的数据,这就离不开事务安全性的考虑。
简单理解事务:就是要使多条DML语句同时成功或失败的一种规则。
如何实现事务
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33# InnoDB存储引擎提供了一组用来记录事务性活动的日志文件
# 在事务的执行过程中,每一条DML的操作都会记录到“事务性活动的日志文件”中
# 在事务的执行过程通过提交事务或者回滚事务就能实现事务要求的规则
# MySQL默认是自动提交事务,即每执行一条DML语句就提交一次,但这不符合开发习惯
# 关闭自动提交机制 start transaction(仅对一次事务有效)
# 或者直接设置开启或关闭自动提交(对所有事务有效)
set autocommit=0; # 关闭自动提交
set autocommit=1; # 开启自动提交
# 提交事务commit,回滚事务rollback
# 准备工作
drop table if exists test;
create table test(
id int primary key auto_increment,
name varchar(20)
);
select * from test;
start transaction; # 关闭自动提交机制,commit之后需要重新执行
insert into test(name) values ('a');
insert into test(name) values ('b');
insert into test(name) values ('c');
select * from test;
rollback; # 回滚事务
select * from test; # 未提交的事务消失
insert into test(name) values ('d');
commit; # 提交事务
select * from test;
rollback;
select * from test; # 已经提交的事务,无法回滚事务的4个特性(ACID)
A:原子性,事务是最小的工作单元,不可再分。
C:一致性,在同一个事务中,所有操作必须同时成功或者失败,保证数据的一致性。
I:隔离性,A事务与B事务之间存在一定的隔离,根据隔离性可分成4个隔离级别。
读未提交:read uncommitted(隔离级别最低)
事务A能够读取事务B未提交的数据,这种隔离级别存在读取到脏数据的问题,即读取到并不需要的数据。一般开发中不会使用这个隔离级别。
读已提交:read committed
事务A只能读取事务B已提交的数据,这种隔离级别避免了脏读问题,但存在不可重复读取数据的问题,事务B再做任何提交操作之后,事务A读取的数据就会相应地发生改变。读取的数据是比较真实的数据,这是Oracle数据库默认的隔离级别。
可重复读:repeatable read
事务B开启后,每次从事务B读取的数据都是一致的,即使事务B的数据修改并提交,事务A所读取到的数据都是事务B刚开始事务时的数据。这种隔离级别避免了不可重复读的问题,但出现了幻影读的问题,即每次读取到的数据都不是当前的真实数据,这是MySQL默认的隔离级别。
序列化/串行化:serializable(最高隔离级别)
效率最低,但解决了上面存在的所有问题。这种隔离级别的事务需要排队,不能并发。synchronized线程同步(事务同步),每次读取到的数据都是最真实的,效率也是最低的。
D:持久性,事务完成的保障,事务提交之后,数据就被持久地保存下来。
隔离级别验证
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77# 在同一个命令行或同一个查询下可能无法展示出隔离级别的效果,建议事务A和事务B各打开一个命令行或者在navicat下各新建一个查询
# 建议每次重新设置隔离级别之后重启命令行或navicat刷新
# 查看隔离级别
select @@tx_isolation;
# 准备工作
create table isolation_level(
name varchar(20)
);
# read uncommitted 读未提交
# 设置全局隔离级别为read uncommitted
set global transaction isolation level read uncommitted;
# 事务A
start transaction;
select * from isolation_level;
# 事务B
start transaction;
insert into isolation_level values ('hello');
# 事务A
select * from isolation_level; # 读取到了未提交的数据
# read committed 读已提交
# 设置全局隔离级别为read committed
set global transaction isolation level read committed;
# 事务A
start transaction;
select * from isolation_level;
# 事务B
start transaction;
insert into isolation_level values ('world');
# 事务A
select * from isolation_level; # 未读取到事务B的数据
# 事务B
commit;
# 事务A
select * from isolation_level; # 读取到了事务B提交的数据
# repeatable read 可重复读
# 设置全局隔离级别为repeatable read
set global transaction isolation level repeatable read;
# 事务A
start transaction;
select * from isolation_level;
# 事务B
start transaction; # 第一次开启事务
insert into isolation_level values ('read');
# 事务A
select * from isolation_level;
# 事务B
insert into isolation_level values ('repeat');
# 事务A
select * from isolation_level;
# 事务B
commit;
# 事务A
select * from isolation_level; # 事务A读取到的始终是事务B第一次开启事务时的数据
# 事务A
commit;
select * from isolation_level; # 事务A也提交后再查询,就能获取事务B的最近一次事务提交的数据
# 补充说明:事务B第一次开启事务这个概念是以事务A开启事务后事务B第一次执行start transaction为准,也就是当事务A提交之后再次开启事务,重新计算事务B第一次开启事务
# serializable 序列化
# 设置全局隔离级别为serializable
set global transaction isolation level serializable;
# 事务A
start transaction;
select * from isolation_level;
# 事务B
start transaction;
insert into isolation_level values ('serializable'); # 卡住,因为事务A没有提交
# 事务A
commit; # 提交事务A后事务B新增数据完成
start transaction; # 事务A重新开启事务
select * from isolation_level; # 卡住或超时报错,因为事务B没有提交
# 事务B
commit; # 提交事务B后事务A查询完成视图
视图(view)
视图的概念
从多个角度观察处理同一份数据,每一个角度就是一种视图。 例如,对同一份数据进行不同的处理,根据不同的需求展示出相应部分的数据或者根据不同的权限展示出每个权限被允许接触到的数据。
视图的使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35# 准备工作
drop table if exists origin;
create table origin(
id int primary key auto_increment,
name varchar(20),
age int,
department varchar(20)
);
insert into origin(name,age,department) values
('apple',13,'developer'),
('orange',14,'operator');
# 先备份原表,复制原表
create table origin_backup select * from origin;
# 创建视图对象,需要as关键字,且只能通过DQL语句创建
create view origin_view as select * from origin;
# 删除视图对象
drop view origin_view;
# 对视图对象的增删改查会映射到原表的数据上
# 查询视图
select * from origin_view;
# 新增视图数据
insert into origin_view(name,age,department) values
('mango',12,'manager'),
('banana',16,'admin');
# 查询原表
select * from origin;
# 更新视图数据
update origin_view set age=20 where name='mango';
select * from origin;
# 删除视图数据
delete from origin_view;
select * from origin;视图对象的实际开发用途
- 视图的使用能够有效简化开发,方便维护,提高开发效率
- 单独的SQL语句只能针对一张数据表的数据进行修改,而视图的使用将多张表的数据集中到一个视图,通过对这个视图数据的修改,就能一次性实现对多张表数据的修改,而不需要每一张数据表重复写一条SQL语句去修改
- 另外对于后期大量数据表的维护,只需要重新修改视图对象所映射的DQL语句就能灵活地指定需要维护的数据表的数据
数据库设计:三大范式
三大范式的概念
范式是数据库数据表的设计依据,是一种规范和统一标准。设计数据库表时,按照以下3种范式进行,可以避免表中数据的冗余,空间的浪费。
第一范式
第一范式:要求任何一张表必须有主键,每一个字段原子性不可再分。
字段原子性是指字段数据不能再拆分,例如,联系方式字段下的数据同时出现手机号和邮箱两个联系方式,说明联系方式字段还能拆分未手机号和邮箱两个字段。
第二范式
第二范式:建立在第一范式的基础之上,要求所有非主键字段完全依赖主键,
不要产生部分依赖。
部分依赖通常出现在多对多逻辑关系(例如多个学生对应多个教师)的数据表中,假设把学生编号和教师编号设置为复合主键,学生依赖学生编号,教师依赖教师编号,这就产生了部分依赖(部分依赖产生的根源就是复合主键的存在,因此设计中一般不建议使用复合主键)。从根本上解决这个问题,就需要将这一张数据表分成学生表,教师表以及关系表三张数据表,并在关系表中设置学生表和教师表两个外键。
第三范式
第三范式:建立在第二范式的基础之上,要求所有非主键字段直接依赖主键,
不要产生传递依赖。
传递依赖通常出现在一对多逻辑关系(例如一个班级对应多个学生)的数据表中,假设学生编号为主键,班级名称依赖班级编号,而一个班级编号依赖多个学生编号,这就产生了传递依赖。为了解决这个问题,就需要将这一张数据表分成学生表和班级表,外键则设置在数据相对较多的数据表中。
数据表设计总结
一对多,两张表,数据多的加外键
多对多,三张表,关系表两个外键
另外对于一对一逻辑关系的数据表,如果字段数过多也建议进行表的拆分。一般拆分成两张表,这时只需要在其中一张表中添加一个唯一性约束(一对一逻辑关系的数据表理论上不可能出现重复的外键字段值)的外键字段即可,外键字段通常链接的是另一张表的主键字段。
最后,数据库设计三大范式也只是理想上的标准。实际开发中,结合实际需求,有必要牺牲一部分存储空间、规范等来换取更高的开发效率和更好的使用体验,这才是数据库数据表设计的最高目的。
- 标题: MySQL高级
- 作者: Entropy Tree
- 创建于 : 2022-08-21 13:09:02
- 更新于 : 2023-09-05 15:21:10
- 链接: https://www.entropy-tree.top/2022/08/21/MySQL-Pro/
- 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。