「写在前面」
本文为黑马程序员 MySQL 教程的学习笔记。本着自己学习、分享他人的态度,分享学习笔记,希望能对大家有所帮助。推荐先按顺序阅读往期内容:
1. MySQL 学习笔记(基础篇 Day1)
2. MySQL 学习笔记(基础篇 Day2)
目录
-
5 多表查询 -
5.1 多表关系 -
5.2 多表查询概述 -
5.3 内连接 -
5.4 外连接 -
5.5 自连接 -
5.6 子查询 -
5.7 多表查询案例
-
-
6 事务 -
6.1 事务简介 -
6.2 事务操作 -
6.3 事务四大特性 -
6.4 并发事务问题 -
6.5 事务隔离级别
-
5 多表查询
5.1 多表关系
P37:https://www.bilibili.com/video/BV1Kr4y1i7ru?p=37
项目开发中,在进行数据库表结构设计时,会根据业务需求及业务模块之间的关系,分析并设计表结构,由于业务之间相互关联,所以各个表结构之间也存在着各种联系,基本上分为三种:
-
一对多(多对一) -
多对多 -
一对一
5.1.1 一对多
-
案例: 部门 与 员工的关系 -
关系: 一个部门对应多个员工,一个员工对应一个部门 -
实现: 在多的一方建立外键,指向一的一方的主键
5.1.2 多对多
-
案例: 学生 与 课程的关系 -
关系: 一个学生可以选修多门课程,一门课程也可以供多个学生选择 -
实现: 建立第三张中间表,中间表至少包含两个外键,分别关联两方主键
对应的SQL脚本:
# 创建 student 表
create table student(
id int auto_increment primary key comment '主键ID',
name varchar(10) comment '姓名',
no varchar(10) comment '学号'
) comment '学生表';
# 向 student 表中插入数据
insert into student values (null, '黛绮丝', '2000100101'),(null, '谢逊', '2000100102'),(null, '殷天正', '2000100103'),(null, '韦一笑', '2000100104');
# 创建 course 表
create table course(
id int auto_increment primary key comment '主键ID',
name varchar(10) comment '课程名称'
) comment '课程表';
# 向 course 表中插入数据
insert into course values (null, 'Java'), (null, 'PHP'), (null , 'MySQL') ,(null, 'Hadoop');
# 创建 student_course 中间表
create table student_course(
id int auto_increment comment '主键' primary key,
studentid int not null comment '学生ID',
courseid int not null comment '课程ID',
constraint fk_courseid foreign key (courseid) references course (id),
constraint fk_studentid foreign key (studentid) references student (id)
) comment '学生课程中间表';
# 插入数据关联两方主键
insert into student_course values (null,1,1),(null,1,2),(null,1,3),(null,2,2),(null,2,3),(null,3,4);
5.1.3 一对一
-
案例: 用户 与 用户详情的关系 -
关系: 一对一关系,多用于单表拆分,将一张表的基础字段放在一张表中,其他详情字段放在另一张表中,以提升操作效率 -
实现: 在任意一方加入外键,关联另外一方的主键,并且设置外键为唯一的(UNIQUE)
5.2 多表查询概述
P38:https://www.bilibili.com/video/BV1Kr4y1i7ru?p=38
多表查询就是指从多张表中查询数据。
原来查询单表数据,执行的SQL形式为:
select * from emp;
而在多表查询中,我们是需要消除无效的笛卡尔积的,只保留两张表关联部分的数据:
select * from emp , dept where emp.dept_id = dept.id;
多表查询分类:
-
连接查询 -
内连接:相当于查询A、B交集部分数据 -
外连接: -
左外连接:查询左表所有数据,以及两张表交集部分数据 -
右外连接:查询右表所有数据,以及两张表交集部分数据
-
-
自连接:当前表与自身的连接查询,自连接必须使用表别名
-
-
子查询
5.3 内连接
P39:https://www.bilibili.com/video/BV1Kr4y1i7ru?p=39
内连接查询的是两张表交集部分的数据
1. 隐式内连接
SELECT 字段列表 FROM 表1 , 表2 WHERE 条件 ... ;
2. 显式内连接
SELECT 字段列表 FROM 表1 [ INNER ] JOIN 表2 ON 连接条件 ... ;
案例:
# A. 查询每一个员工的姓名 , 及关联的部门的名称 (隐式内连接实现)
select emp.name , dept.name from emp , dept where emp.dept_id = dept.id ;
# B. 查询每一个员工的姓名 , 及关联的部门的名称 (显式内连接实现)
select e.name, d.name from emp e inner join dept d on e.dept_id = d.id;
5.4 外连接
P40:https://www.bilibili.com/video/BV1Kr4y1i7ru?p=40
1. 左外连接
左外连接相当于查询表1(左表)的所有数据,当然也包含表1和表2交集部分的数据。
SELECT 字段列表 FROM 表1 LEFT [ OUTER ] JOIN 表2 ON 条件 ... ;
2. 右外连接
右外连接相当于查询表2(右表)的所有数据,当然也包含表1和表2交集部分的数据。
SELECT 字段列表 FROM 表1 RIGHT [ OUTER ] JOIN 表2 ON 条件 ... ;
案例:
# A. 查询emp表的所有数据, 和对应的部门信息
select e.*, d.name from emp e left outer join dept d on e.dept_id = d.id;
# B. 查询dept表的所有数据, 和对应的员工信息(右外连接)
select d.*, e.* from emp e right outer join dept d on e.dept_id = d.id;
5.5 自连接
5.5.1 自连接查询
P41:https://www.bilibili.com/video/BV1Kr4y1i7ru?p=41
自连接查询,顾名思义,就是自己连接自己,也就是把一张表连接查询多次。
自连接的查询语法:
SELECT 字段列表 FROM 表A 别名A JOIN 表A 别名B ON 条件 ... ;
案例:
# A. 查询员工 及其 所属领导的名字
select a.name , b.name from emp a , emp b where a.managerid = b.id;
# B. 查询所有员工 emp 及其领导的名字 emp , 如果员工没有领导, 也需要查询出来
select a.name '员工', b.name '领导' from emp a left join emp b on a.managerid = b.id;
注意事项:在自连接查询中,必须要为表起别名,要不然我们不清楚所指定的条件、返回的字段,到底是哪一张表的字段。
5.5.2 联合查询
P42:https://www.bilibili.com/video/BV1Kr4y1i7ru?p=42
对于union查询,就是把多次查询的结果合并起来,形成一个新的查询结果集。
语法:
SELECT 字段列表 FROM 表A ...
UNION [ ALL ]
SELECT 字段列表 FROM 表B ....;
-
对于联合查询的多张表的列数必须保持一致,字段类型也需要保持一致。 -
union all 会将全部的数据直接合并在一起,union 会对合并之后的数据去重。
案例:
# A. 将薪资低于 5000 的员工 , 和 年龄大于 50 岁的员工全部查询出来
select * from emp where salary < 5000
union
select * from emp where age > 50;
union 联合查询,会对查询出来的结果进行去重处理。
注意:如果多条查询语句查询出来的结果,字段数量不一致,在进行union/union all联合查询时,将会报错。
5.6 子查询
5.6.1 概述
P43:https://www.bilibili.com/video/BV1Kr4y1i7ru?p=43
1. 概念
SQL语句中嵌套SELECT语句,称为嵌套查询,又称子查询。
SELECT * FROM t1 WHERE column1 = ( SELECT column1 FROM t2 );
子查询外部的语句可以是INSERT / UPDATE / DELETE / SELECT 的任何一个
2. 分类
根据子查询结果不同,分为:
A. 标量子查询(子查询结果为单个值)
B. 列子查询(子查询结果为一列)
C. 行子查询(子查询结果为一行)
D. 表子查询(子查询结果为多行多列)
根据子查询位置,分为:
A. WHERE之后
B. FROM之后
C. SELECT之后
5.6.2 标量子查询
P44:https://www.bilibili.com/video/BV1Kr4y1i7ru?p=44
子查询返回的结果是单个值(数字、字符串、日期等),最简单的形式,这种子查询称为标量子查询。
常用的操作符:= <> > >= < <=
案例:
# A. 查询 "销售部" 的所有员工信息
select * from emp where dept_id = (select id from dept where name = '销售部');
# B. 查询在 "方东白" 入职之后的员工信息
select * from emp where entrydate > (select entrydate from emp where name = '方东白');
5.6.3 列子查询
P45:https://www.bilibili.com/video/BV1Kr4y1i7ru?p=45
子查询返回的结果是一列(可以是多行),这种子查询称为列子查询。
常用的操作符:IN 、NOT IN 、 ANY 、SOME 、 ALL
操作符 | 描述 |
---|---|
IN | 在指定的集合范围之内,多选一 |
NOT | IN 不在指定的集合范围之内 |
ANY | 子查询返回列表中,有任意一个满足即可 |
SOME | 与ANY等同,使用SOME的地方都可以使用ANY |
ALL | 子查询返回列表的所有值都必须满足 |
案例:
# A. 查询 "销售部" 和 "市场部" 的所有员工信息
select * from emp where dept_id in (select id from dept where name = '销售部' or name = '市场部');
# B. 查询比 财务部 所有人工资都高的员工信息
select * from emp where salary > all ( select salary from emp where dept_id = (select id from dept where name = '财务部') );
# C. 查询比研发部其中任意一人工资高的员工信息
select * from emp where salary > any ( select salary from emp where dept_id = (select id from dept where name = '研发部') );
5.6.4 行子查询
P46:https://www.bilibili.com/video/BV1Kr4y1i7ru?p=46
子查询返回的结果是一行(可以是多列),这种子查询称为行子查询。
常用的操作符:= 、<> 、IN 、NOT IN
案例:
# A. 查询与 "张无忌" 的薪资及直属领导相同的员工信息 ;
select * from emp where (salary,managerid) = (select salary, managerid from emp where name = '张无忌');
5.6.5 表子查询
P47:https://www.bilibili.com/video/BV1Kr4y1i7ru?p=47
子查询返回的结果是多行多列,这种子查询称为表子查询。
常用的操作符:IN
案例:
# A. 查询与 "鹿杖客" , "宋远桥" 的职位和薪资相同的员工信息
select * from emp where (job,salary) in ( select job, salary from emp where name = '鹿杖客' or name = '宋远桥' );
# B. 查询入职日期是 "2006-01-01" 之后的员工信息 , 及其部门信息
select e.*, d.* from (select * from emp where entrydate > '2006-01-01') e left join dept d on e.dept_id = d.id ;
5.7 多表查询案例
P48:https://www.bilibili.com/video/BV1Kr4y1i7ru?p=48
P49:https://www.bilibili.com/video/BV1Kr4y1i7ru?p=49
P50:https://www.bilibili.com/video/BV1Kr4y1i7ru?p=50
6 事务
6.1 事务简介
P51:https://www.bilibili.com/video/BV1Kr4y1i7ru?p=51
事务是一组操作的集合,它是一个不可分割的工作单位,事务会把所有的操作作为一个整体一起向系统提交或撤销操作请求,即这些操作要么同时成功,要么同时失败。
就比如: 张三给李四转账 1000 块钱,张三银行账户的钱减少 1000,而李四银行账户的钱要增加 1000。这一组操作就必须在一个事务的范围内,要么都成功,要么都失败。
注意: 默认MySQL的事务是自动提交的,也就是说,当执行完一条DML语句时,MySQL会立即隐式的提交事务。
6.2 事务操作
P52:https://www.bilibili.com/video/BV1Kr4y1i7ru?p=52
数据准备:
drop table if exists account;
create table account(
id int primary key AUTO_INCREMENT comment 'ID',
name varchar(10) comment '姓名',
money double(10,2) comment '余额'
) comment '账户表';
insert into account(name, money) VALUES ('张三',2000), ('李四',2000);
6.2.1 未控制事务
1. 测试正常情况
-- 1. 查询张三余额
select * from account where name = '张三';
-- 2. 张三的余额减少1000
update account set money = money - 1000 where name = '张三';
-- 3. 李四的余额增加1000
update account set money = money + 1000 where name = '李四';
测试完毕之后检查数据的状态, 可以看到数据操作前后是一致的
2. 测试异常情况
-- 1. 查询张三余额
select * from account where name = '张三';
-- 2. 张三的余额减少1000
update account set money = money - 1000 where name = '张三';
出错了....
-- 3. 李四的余额增加1000
update account set money = money + 1000 where name = '李四';
我们把数据都恢复到2000, 然后再次一次性执行上述的SQL语句(出错了.... 这句话不符合SQL语法,执行就会报错),检查最终的数据情况, 发现数据在操作前后不一致了
6.2.2 控制事务一
1. 查看/设置事务提交方式
SELECT @@autocommit ;
SET @@autocommit = 0 ;
2. 提交事务
COMMIT;
3. 回滚事务
ROLLBACK;
注意:上述的这种方式,我们是修改了事务的自动提交行为, 把默认的自动提交修改为了手动提交, 此时我们执行的DML语句都不会提交, 需要手动的执行commit进行提交。
6.2.3 控制事务二
1. 开启事务
START TRANSACTION 或 BEGIN ;
2. 提交事务
COMMIT;
3. 回滚事务
ROLLBACK;
转账案例:
-- 开启事务
start transaction
-- 1. 查询张三余额
select * from account where name = '张三';
-- 2. 张三的余额减少1000
update account set money = money - 1000 where name = '张三';
-- 3. 李四的余额增加1000
update account set money = money + 1000 where name = '李四';
-- 如果正常执行完毕, 则提交事务
commit;
-- 如果执行过程中报错, 则回滚事务
-- rollback;
6.3 事务四大特性
P53:https://www.bilibili.com/video/BV1Kr4y1i7ru?p=53
-
原子性(Atomicity):事务是不可分割的最小操作单元,要么全部成功,要么全部失败。 -
一致性(Consistency):事务完成时,必须使所有的数据都保持一致状态。 -
隔离性(Isolation):数据库系统提供的隔离机制,保证事务在不受外部并发操作影响的独立环境下运行。 -
持久性(Durability):事务一旦提交或回滚,它对数据库中的数据的改变就是永久的。
上述就是事务的四大特性,简称ACID。
6.4 并发事务问题
P54:https://www.bilibili.com/video/BV1Kr4y1i7ru?p=54
1. 赃读
一个事务读到另外一个事务还没有提交的数据。
比如B读取到了A未提交的数据
2. 不可重复读
一个事务先后读取同一条记录,但两次读取的数据不同,称之为不可重复读
事务A两次读取同一条记录,但是读取到的数据却是不一样的
3. 幻读
一个事务按照条件查询数据时,没有对应的数据行,但是在插入数据时,又发现这行数据已经存在,好像出现了 "幻影"
6.5 事务隔离级别
P55:https://www.bilibili.com/video/BV1Kr4y1i7ru?p=55
为了解决并发事务所引发的问题,在数据库中引入了事务隔离级别。主要有以下几种:
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
Read uncommitted | √ | √ | √ |
Read committed | × | √ | √ |
Repeatable Read(默认) | × | × | √ |
Serializable | × | × | × |
1. 查看事务隔离级别
SELECT @@TRANSACTION_ISOLATION;
2. 设置事务隔离级别
SET [ SESSION | GLOBAL ] TRANSACTION ISOLATION LEVEL { READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE }
注意:事务隔离级别越高,数据越安全,但是性能越低。
事物小结
P56:https://www.bilibili.com/video/BV1Kr4y1i7ru?p=56
基础篇总结
P57:https://www.bilibili.com/video/BV1Kr4y1i7ru?p=57
注:本文为个人学习笔记,仅供大家参考学习,不得用于任何商业目的。如有侵权,请联系作者删除。
本文由 mdnice 多平台发布