MySQL
引入
1.数据库基本概念
1.1 数据
所谓数据(Data)是指对客观事物进行描述并可以鉴别的符号,这些符号是可识别的、抽象的。它不仅仅指狭义上的数字,而是有多种表现形式:字母、文字、文本、图形、音频、视频等。现在计算机存储和处理的数据范围十分广泛,而描述这些数据的符号也变得越来越复杂了。
1.2 数据库
数据库(Database,DB)指的是以一定格式存放、能够实现多个用户共享、与应用程序彼此独立的数据集合。
1.3 数据库管理系统
数据库管理系统(Database Management System,DBMS)是用来定义和管理数据的软件。如何科学的组织和存储数据,如何高效的获取和维护数据,如何保证数据的安全性和完整性,这些都需要靠数据库管理系统完成。目前,比较流行的数据库管理系统有:Oracle、MySQL、SQL Server、DB2等。
1.4数据库应用程序
数据库应用程序(Database Application System,DBAS)是在数据库管理系统基础上,使用数据库管理系统的语法,开发的直接面对最终用户的应用程序,如学生管理系统、人事管理系统、图书管理系统等。
1.5 数据库管理员
数据库管理员(Database Administrator,DBA)是指对数据库管理系统进行操作的人员,其主要负责数据库的运营和维护。
1.6 最终用户
最终用户(User)指的是数据库应用程序的使用者。用户面向的是数据库应用程序(通过应用程序操作数据),并不会直接与数据库打交道。
1.7 数据库系统
数据库系统(Database System,DBS)一般是由数据库、数据库管理系统、数据库应用程序、数据库管理员和最终用户构成。其中DBMS是数据库系统的基础和核心
2. 数据库表列类型
2.1 整数类型

MySQL支持选择在该类型关键字后面的括号内指定整数值的显示宽度(例如,INT(4))。显示宽度并不限制可以在列内保存的值的范围,也不限制超过列的指定宽度的值的显示
主键自增:不使用序列,通过auto_increment,要求是整数类型
支持选择在该类型关键字后面的括号内指定整数值的显示宽度

表的完整性约束
1. 非外键约束
约束条件 | 约束描述 |
---|---|
PRIMARY KEY | 主键约束,约束字段的值可唯一地标识对应的记录 |
NOT NULL | 非空约束,约束字段的值不能为空 |
UNIQUE | 唯一约束,约束字段的值是唯一的 |
CHECK | 检查约束,限制某个字段的取值范围 |
DEFAULT | 默认值约束,约束字段的默认值 |
AUTO_INCREMENT | 自动增加约束,约束字段的值自动递增 |
FOREIGN KEY | 外键约束,约束表与表之间的关系 |
实例一:建立一张用来存储学生信息的表
1 | /* |

添加数据:
1
2
3
4
5
6
7
8
9
10
11
12
13
14-- 添加数据:
-- 1048 - Column 'sname' cannot be null 不能为null
-- 3819 - Check constraint 't_student_chk_1' is violated. 违反检查约束
insert into t_student values (1,'张三','男',21,'2023-9-1','java01班','zs@126.com');
-- 1062 - Duplicate entry '1' for key 't_student.PRIMARY' 主键重复
-- > 1062 - Duplicate entry 'ls@126.com' for key 't_student.email' 违反唯一约束
insert into t_student values (2,'李四','男',21,'2023-9-1','java01班','ls@126.com');
insert into t_student values (3,'露露','男',21,'2023-9-1','java01班','ls@126.com');
-- 如果主键没有设定值,或者用null.default都可以完成主键自增的效果
insert into t_student (sname,enterdate) values ('菲菲','2029-4-5');
insert into t_student values (null,'小明','男',21,'2023-9-1','java01班','xm@126.com');
insert into t_student values (default,'小刚','男',21,'2023-9-1','java01班','xg@126.com');
-- 如果sql报错,可能主键就浪费了,后续插入的主键是不连号的,我们主键也不要求连号的
insert into t_student values (null,'小明','男',21,'2023-9-1','java01班','oo@126.com');常见错误:
–》 1048 - Column ‘sname’ cannot be null 不能为null
–》 819 - Check constraint ‘t_student_chk_1’ is violated. 违反检查约束
– 》1062 - Duplicate(重复的) entry ‘1’ for key ‘t_student.PRIMARY’ 主键重复
– 》1062 - Duplicate entry ‘ls@126.com‘ for key ‘t_student.email’ 违反唯一约束
- 使用自增的时候用null和default都是可以
- 如果sql报错,可能主键就浪费了,后续插入的主键是不连号的,主键也不要求连号的
- 列级约束和表级约束
(1) 表级约束:可以约束表中任意一个或多个字段。与列定义相互独立,不包含在列定义中;与定义用‘,’分隔;必须指出要约束的列的名称;
(2) 列级约束:包含在列定义中,直接跟在该列的其它定义之后 ,用空格分隔;不必指定列名;
实例二
1 | -- 删除表: |
- 创建表之后添加约束
实例三
1 | -- 删除表: |
验证约束添加成功:

2. 外键约束
- 外键约束(FOREIGN KEY,缩写FK)是用来实现数据库表的参照完整性的。外键约束可以使两张表紧密的结合起来,特别是针对修改或者删除的级联操作时,会保证数据的完整性。
- 外键是指表中某个字段的值依赖于另一张表中某个字段的值,而被依赖的字段必须具有主键约束或者唯一约束。被依赖的表我们通常称之为父表或者主表,设置外键约束的表称为子表或者从表。
- 只有表级约束没有列级约束
实例四
主表(父表):班级表 - 班级编号 - 主键
从表(子表):学生表 - 班级编号 - 外键
创建父表—班级表:
cno(主键、自增);cname(不为空);room;
1
2
3
4
5
6
7
8
9-- 先删除表,先删除从表再删除父表
drop table t_student;
drop table t_class;
-- 先创建父表:班级表:
create table t_class(
cno int(4) primary key auto_increment,
cname varchar(10) not null,
room char(4)
)
创建从表—学生表:
sno(主键、自增);sname(不为空);classno(取值参考t_class表中的cno字段,不要求字段名字完全重复,但是类型长度定义 尽量要求相同。);
添加外键约束:
1
2
3
4
5
6
7
8
9-- 添加学生表,添加外键约束:
create table t_student(
sno int(6) primary key auto_increment,
sname varchar(5) not null,
classno int(4),-- 取值参考t_class表中的cno字段,不要求字段名字完全重复,但是类型长度定义 尽量要求相同。
-- 外键约束:
constraint fk_stu_classno foreign key (classno) references t_class (cno)
);
删除外键约束
1
2
3-- 删除外键约束
alter table EMP
drop foreign key FK_DEPTNO
添加数据:
可以一次性添加多条记录:
1
2
3-- 可以一次性添加多条记录:
insert into t_class values (null,'java001','r803'),(null,'java002','r416'),(null,'大数据001','r103');
insert into t_student values (null,'张三',1),(null,'李四',1),(null,'王五',2),(null,'朱六',3);查询班级表:
1
2
3-- 查看班级表和学生表:
select * from t_class;
select * from t_student;添加外键的效果:
- 删除班级会出错
1
2-- 尝试删除班级
delete from t_class where cno = 1;
外键策略:
直接删除班级会错误,有时候必须删,可以加入外键策略
策略一:no action 不允许操作
1 | -- 策略1:no action 不允许操作 |
结果:


策略二:使用cascade级联操作
操作主表的时候影响从表的外键
先删除外键约束
1
2-- 策略2:cascade 级联操作:操作主表的时候影响从表的外键信息:
alter table t_student drop foreign key fk_stu_classno;重新添加外键约束(增加一个级联操作)
1
2-- 重新添加外键约束:
alter table t_student add constraint fk_stu_classno foreign key (classno) references t_class (cno) on update cascade on delete cascade;试着更新和删除操作:
1
2
3
4-- 试试更新:
update t_class set cno = 5 where cno = 3;
-- 试试删除:
delete from t_class where cno = 5;结果
策略三:set null 置空操作
先删除之前的外键约束
1
2-- 先删除之前的外键约束:
alter table t_student drop foreign key fk_stu_classno;重新添加外键约束
1
2-- 重新添加外键约束:
alter table t_student add constraint fk_stu_classno foreign key (classno) references t_class (cno) on update set null on delete set null;试着更新操作
1
2-- 更新
update t_class set cno = 5 whwere con = 1;结果:
注:策略2 级联操作 和 策略2 的 删除操作 可以混着使用
1 | alter table t_student add constraint fk_stu_classno foreign key (classno) references t_class (cno) on update cascade on delete set null ; |
应用场合:
– (1)朋友圈删除,点赞。留言都删除 – 级联操作
– (2)解散班级,对应的学生 置为班级为null就可以了,– set null
DQL查询操作*
1. 常见表的准备
部门表(dept)
1 | create table DEPT( |
员工表(emp)
1 | create table EMP |
薪资登记表(salgrade)
1 | create table SALGRADE |
奖金表(bonus)
1 | create table BONUS |
操作
1 | -- DEPT |
结果(表格形式)




2. 单表查询
2.1 简单的SQL查询
1 | -- 对emp表查询: |
2.2 where子句
将过滤条件放在where子句的后面,可以筛选/过滤出我们想要的符合条件的数据
加关系运算符
1
2
3
4
5
6
7
8
9
10
11
12-- where 子句 + 关系运算符
select * from emp where deptno = 10;
select * from emp where deptno > 10;
select * from emp where deptno >= 10;
select * from emp where deptno < 10;
select * from emp where deptno <= 10;
select * from emp where deptno <> 10;
select * from emp where deptno != 10;
select * from emp where job = 'CLERK';
select * from emp where job = 'clerk'; -- 默认情况下不区分大小写
select * from emp where binary job = 'clerk'; -- binary区分大小写
select * from emp where hiredate < '1981-12-25';加逻辑运算符
1
2
3
4
5
6
7
8
9
10-- where 子句 + 逻辑运算符:and
select * from emp where sal > 1500 and sal < 3000; -- (1500,3000)
select * from emp where sal > 1500 && sal < 3000;
select * from emp where sal > 1500 and sal < 3000 order by sal;
select * from emp where sal between 1500 and 3000; -- [1500,3000]
-- where 子句 + 逻辑运算符:or
select * from emp where deptno = 10 or deptno = 20;
select * from emp where deptno = 10 || deptno = 20;
select * from emp where deptno in (10,20);
select * from emp where job in ('MANAGER','CLERK','ANALYST');加模糊查询
1
2
3
4
5-- where子句 + 模糊查询:
-- 查询名字中带A的员工 -- %代表任意多个字符 0,1,2,.....
select * from emp where ename like '%A%' ;
-- -任意一个字符
select * from emp where ename like '__A%' ;关于null的判断
1
2
3-- 关于null的判断:
select * from emp where comm is null;
select * from emp where comm is not null;小括号的使用:因为不同运算符的优先级别不同,加括号为了可读性
1
2
3
4-- 小括号的使用 :因为不同的运算符的优先级别不同,加括号为了可读性
select * from emp where job = 'SALESMAN' or job = 'CLERK' and sal >=1500; -- 先and再or and > or
select * from emp where job = 'SALESMAN' or (job = 'CLERK' and sal >=1500);
select * from emp where (job = 'SALESMAN' or job = 'CLERK') and sal >=1500;
2.3 函数
除了多行函数(max,min,count,sum,avg),都是单行函数
- 单行函数
1. 字符串函数
函数 | 描述 |
---|---|
CONCAT(str1, str2, ···, strn) | 将str1、str2···strn拼接成一个新的字符串 |
INSERT(str, index, n, newstr) | 将字符串str从第index位置开始的n个字符替换成字符串newstr |
LENGTH(str) | 获取字符串str的长度 |
LOWER(str) | 将字符串str中的每个字符转换为小写 |
UPPER(str) | 将字符串str中的每个字符转换为大写 |
LEFT(str, n) | 获取字符串str最左边的n个字符 |
RIGHT(str, n) | 获取字符串str最右边的n个字符 |
LPAD(str, n, pad) | 使用字符串pad在str的最左边进行填充,直到长度为n个字符为止 |
RPAD(str, n, pad) | 使用字符串pad在str的最右边进行填充,直到长度为n个字符为止 |
LTRIM(str) | 去除字符串str左侧的空格 |
RTRIM(str) | 去除字符串str右侧的空格 |
TRIM(str) | 去除字符串str左右两侧的空格 |
REPLACE(str,oldstr,newstr) | 用字符串newstr替换字符串str中所有的子字符串oldstr |
REVERSE(str) | 将字符串str中的字符逆序 |
STRCMP(str1, str2) | 比较字符串str1和str2的大小 |
SUBSTRING(str,index,n) | 获取从字符串str的index位置开始的n个字符 |
1 | -- 1.字符串函数 |
显示结果:

2. 数值函数
函数 | 描述 |
---|---|
ABS(num) | 返回num的绝对值 |
CEIL(num) | 返回大于num的最小整数(向上取整) |
FLOOR(num) | 返回小于num的最大整数(向下取整) |
MOD(num1, num2) | 返回num1/num2的余数(取模) |
PI() | 返回圆周率的值 |
POW(num,n)/POWER(num, n) | 返回num的n次方 |
RAND(num) | 返回0~1之间的随机数 |
ROUND(num, n) | 返回x四舍五入后的值,该值保留到小数点后n位 |
TRUNCATE(num, n) | 返回num被舍去至小数点后n位的值 |
1 | -- 2.数值函数 |
显示结果:

3. 日期与时间函数
函数 | 描述 |
---|---|
CURDATE() | 返回当前日期 |
CURTIME() | 返回当前时间 |
NOW() | 返回当前日期和时间 |
SYSDATE() | 返回该函数执行时的日期和时间 |
DAYOFYEAR(date) | 返回日期date为一年中的第几天 |
WEEK(date)/WEEKOFYEAR(date) | 返回日期date为一年中的第几周 |
DATE_FORMAT(date, format) | 返回按字符串format格式化后的日期date |
DATE_ADD(date, INTERVAL expr unit) /ADDDATE(date, INTERVAL expr unit) | 返回date加上一个时间间隔后的新时间值 |
DATE_SUB(date, INTERVAL expr unit) /SUBDATE(date, INTERVAL expr unit) | 返回date减去一个时间间隔后的新时间值 |
DATEDIFF(date1, date2) | 返回起始日期date1与结束日期date2之间的间隔天数 |
1 | -- 3.日期与时间函数 |



4. 流程函数

1 | -- 4.流程函数 |
5. JSON函数

6. 其他函数

1 | -- 6.其他函数 |
- 多行函数
对一组数据进行运算,针对一组数据(多行记录)只返回一个结果,也称分组函数

1 | -- 多行函数: |
2.4 分组与筛选
group by 分组
1 | select * from emp; |
having 分组后筛选
1 | -- 统计各个部门的平均工资 ,只显示平均工资2000以上的 - 分组以后进行二次筛选 having |
where在分组前进行过滤的,having在分组后进行后滤。
对比实例:
1 | -- 对比实例:统计各个岗位的平均工资,除了MANAGER |
总结
[1] select语句总结
1 | select column, group_function(column) |
注意:顺序固定,不可以改变顺序
[2] select语句的执行顺序
from — where — group by — select — having — order by
[3] 单表查询练习
1 | -- 单表查询练习: |
3. 多表查询
3.1 交叉连接 cross join
1 | select * |
3.2 自然连接 natural
1 | select * |
优点:自动匹配所有的同名列 ,同名列只显示一次,简单
缺点:查询字段的时候,没有指定字段对应的数据库表,效率低
解决——指定表名
1 | select emp.empno,emp.enaem,emp.sal,dept.dname,dept.loc,dept.deptno |
缺点:表名太长
解决——起表名
1 | select e.empno,e.ename,e.sal,d.dname,d.loc,d.deptno |
缺点:会自动匹配所有表中同名的字段。有时候我们只需要部分相同的字段
3.3 内连接 inner join
1 | select * |
缺点:连接的表字段不同时using无法使用。
解决——使用on子句
1 | select * |
3.4 外连接 outer join
- 内连接只显示匹配的数据,例如一个部门没有员工就不会显示
- 外连接可以显示不匹配的数据
- outer都可以省略的
左外连接
左边的那个表的信息,即使不匹配也可以查看出结果
1 | select * |
结果

右外连接
右边的那个表的信息,即使不匹配也可以查看出结果
1 | select * |
结果

全外连接
这个语法在mysql里面不支持,oracle才支持
为了代替 可以取左外连接和右外连接的并集
并集 union
1 | select * |
结果

union all
1 | select * |
结果

3.5 三表查询
案例:查询员工的编号、姓名、薪水、部门编号、部门名称、薪水等级
1 | select * from emp; |
结果

3.6 自连接查询
相当于把本身表复制一份,本质与前面的连接没有区别
1 | -- 查询员工的编号、姓名、上级编号,上级的姓名 |
结果

补充:92语法的多表查询
1 | -- 查询员工的编号,员工姓名,薪水,员工部门编号,部门名称: |
4. 不相关子查询
引入
要查询所有比“CLARK”工资高的员工的信息
需要两个命令而且第二个命令依托于第一个命令,会相互影响
1 | -- 命令一:查询clark的工资 |
子查询可以通过一个命令解决问题 !
4.1 单行子查询
1 | -- 将上面两个命令合并 |
4.2 多行子查询
子查询查出来的记录为多条:
实例一
- 查询部门20中职务同部门10的雇员一样的雇员信息。
1 | -- 查询雇员信息 |
如果使用单行子查询,就会报错 ->Subquery returns more than 1 row
1 | select * from emp |
多行子查询可以解决
1 | select * from emp |
实例二
查询工资比所有的“SALESMAN”都高的雇员的编号、名字和工资
1
2
3
4
5
6
7
8
9-- 查询雇员的编号、名字和工资
select empno,ename,sal from emp
-- “SALESMAN”的工资:
select sal from emp where job = 'SALESMAN'
-- 查询工资比所有的“SALESMAN”都高的雇员的编号、名字和工资。
-- 多行子查询:
select empno,ename,sal
from emp
where sal > all(select sal from emp where job = 'SALESMAN');1
2
3
4-- 单行子查询:
select empno,ename,sal
from emp
where sal > (select max(sal) from emp where job = 'SALESMAN');
实例三
- 查询工资低于任意一个“CLERK”的工资的雇员信息。
1 | -- 查询雇员信息 |
1 | -- 单行子查询: |
5. 相关子查询
引入
不相关的子查询引入:
不相关的子查询:子查询可以独立运行,先运行子查询,再运行外查询。
相关子查询:子查询不可以独立运行,并且先运行外查询,再运行子查询不相关的子查询优缺点:
好处:简单 功能强大(一些使用不相关子查询不能实现或者实现繁琐的子查询,可以使用相关子查询实现)
缺点:稍难理解
实例
查询各个部门最高工资的员工
- 不相关子查询
1
2
3
4
5select * from emp where deptno = 10 and sal = (select max(sal) from emp where deptnp = 10)
union
select * from emp where deptno = 20 and sal = (select max(sal) from emp where deptno = 20)
union
select * from emp where deptno = 30 and sal = (select max(sal) from emp where deptno = 30);太复杂了:unamused:
- 相关子查询
1
select * from emp e where sal = (select max(sal) from emp where deptno = e.deptno)
简单多啦:happy:
查询工高于所在岗位的平均工资的那些员工
- 不相关子查询
1
2
3select * from emp where job = '' and sal >= (select avg(sal) from emp where job = '')
union ....- 相关子查询
1
select * from emp e where sal >= (select avg(sal) from emp where job = e.job);
数据库对象
1. 事务
事务(Transaction)是用来维护数据库完整性的,它能够保证一系列的MySQL操作要么全部执行,要么全不执行。
eg1.
转账操作:A账户要转账给B账户,那么A账户上减少的钱数和B账户上增加的钱数必须一致,也就是说A账户的转出操作和B账户的转入操作要么全部执行,要么全不执行;如果其中一个操作出现异常而没有执行的话,就会导致账户A和账户B的转入转出金额不一致的情况,为而事实上这种情况是不允许发生的,所以为了防止这种情况的发生,需要使用事务处理。
eg2.
在淘宝购物下订单的时候,商家库存要减少,订单增加记录,付款我的账号少100元…操作要么全部执行,要么全不执行
概念
事务(Transaction)指的是一个操作序列,该操作序列中的多个操作要么都做,要么都不做,是一个不可分割的工作单位,是数据库环境中的逻辑工作单位,由DBMS(数据库管理系统)中的事务管理子系统负责事务的处理。
目前常用的存储引擎有InnoDB(MySQL5.5以后默认的存储引擎)和MyISAM(MySQL5.5之前默认的存储引擎),其中InnoDB支持事务处理机制,而MyISAM不支持。
特性

事务处理可以确保除非事务性序列内的所有操作都成功完成,否则不会永久更新面向数据的资源。通过将一组相关操作组合为一个要么全部成功要么全部失败的序列,可以简化错误恢复并使应用程序更加可靠。
但并不是所有的操作序列都可以称为事务,这是因为一个操作序列要成为事务,必须满足事务的原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability)。这四个特性简称为ACID特性。
- 原子性
原子是自然界最小的颗粒,具有不可再分的特性。事务中的所有操作可以看做一个原子,事务是应用中不可再分的最小的逻辑执行体。 使用事务对数据进行修改的操作序列,要么全部执行,要么全不执行。通常,某个事务中的操作都具有共同的目标,并且是相互依赖的。如果数据库系统只执行这些操作中的一部分,则可能会破坏事务的总体目标,而原子性消除了系统只处理部分操作的可能性。
- 一致性
一致性是指事务执行的结果必须使数据库从一个一致性状态,变到另一个一致性状态。当数据库中只包含事务成功提交的结果时,数据库处于一致性状态。一致性是通过原子性来保证的。
例如:在转账时,只有保证转出和转入的金额一致才能构成事务。也就是说事务发生前和发生后,数据的总额依然匹配。 - 隔离性
隔离性是指各个事务的执行互不干扰,任意一个事务的内部操作对其他并发的事务,都是隔离的。也就是说:并发执行的事务之间既不能看到对方的中间状态,也不能相互影响。
例如:在转账时,只有当A账户中的转出和B账户中转入操作都执行成功后才能看到A账户中的金额减少以及B账户中的金额增多。并且其他的事务对于转账操作的事务是不能产生任何影响的。 - 持久性
持久性指事务一旦提交,对数据所做的任何改变,都要记录到永久存储器中,通常是保存进物理数据库,即使数据库出现故障,提交的数据也应该能够恢复。但如果是由于外部原因导致的数据库故障,如硬盘被损坏,那么之前提交的数据则有可能会丢失。
实例:使用事务保证转账安全
1 | -- 创建账户表:id\uname\balance |
必须让上面的两个操作控制在一个事务中
1 | -- 手动开启事务: |
在回滚和提交之前,数据库中的数据都是操作的缓存中的数据,而不是数据库的真实数据
事务并发问题
脏读(Dirty read)
当一个事务正在访问数据并且对数据进行了修改,而这种修改还没有提交到数据库中,这时另外一个事务也访问了这个数据,然后使用了这个数据。因为这个数据是还没有提交的数据,那么另外一个事务读到的这个数据是“脏数据”,依据“脏数据”所做的操作可能是不正确的。

不可重复读 (Unrepeatableread)
指在一个事务内多次读同一数据。在这个事务还没有结束时,另一个事务也访问该数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改导致第一个事务两次读取的数据可能不太一样。这就发生了在一个事务内两次读到的数据是不一样的情况,因此称为不可重复读。

幻读 (Phantom read)
幻读与不可重复读类似。它发生在一个事务(T1)读取了几行数据,接着另一个并发事务(T2)插入了一些数据时。在随后的查询中,第一个事务(T1)就会发现多了一些原本不存在的记录,就好像发生了幻觉一样,所以称为幻读。

不可重复度和幻读区别:
不可重复读的重点是修改,幻读的重点在于新增或者删除。解决不可重复读的问题只需锁住满足条件的行,解决幻读需要锁表 。
例1(同样的条件, 你读取过的数据, 再次读取出来发现值不一样了 ):事务1中的A先生读取自己的工资为 1000的操作还没完成,事务2中的B先生就修改了A的工资为2000,导 致A再读自己的工资时工资变为 2000;这就是不可重复读。
例2(同样的条件, 第1次和第2次读出来的记录数不一样 ):假某工资单表中工资大于3000的有4人,事务1读取了所有工资大于3000的人,共查到4条记录,这时事务2 又插入了一条工资大于3000的记录,事务1再次读取时查到的记录就变为了5条,这样就导致了幻读
事务隔离级别
事务的隔离级别用于决定如何控制并发用户读写数据的操作。数据库是允许多用户并发访问的,如果多个用户同时开启事务并对同一数据进行读写操作的话,有可能会出现脏读、不可重复读和幻读问题,所以MySQL中提供了四种隔离级别来解决上述问题。
从低到高依次为READ UNCOMMITTED、READ COMMITTED、REPEATABLE READ以及SERIALIZABLE,隔离级别越低,越能支持高并发的数据库操作。

1 | -- 查看默认的事务隔离级别 MySQL默认的是repeatable read |
实例
(必须是两个事务都开始以后执行的操作才会影响)
在read uncommitted的情况下,开始两个事务:
- 事务一
- 事务二
事务一查询操作:
事务二更新操作,且没提交
事务一再次查询:
读到了在脏数据
如果设置为
1
set session transaction isolation level read committed;
事务二就不会读到脏数据。
2. 视图
概念
视图(view)是一个从单张或多张基础数据表或其他视图中构建出来的虚拟表。同基础表一样,视图中也包含了一系列带有名称的列和行数据,但是数据库中只是存放视图的定义,也就是动态检索数据的查询语句,而并不存放视图中的数据,这些数据依旧存放于构建视图的基础表中,只有当用户使用视图是才去数据库请求相对应的数据,即视图中的数据是在引用视图时动态生成的。因此视图中的数据发生了变化,视图中相应的数据也会跟着改变。
PS:视图本质上就是:一个查询语句,是一个虚拟的表,不存在的表,你查看视图,其实就是查看视图对应的sql语句。
优点
简化用户操作:视图可以使用户将注意力集中在所关心地数据上,而不需要关心数据表的结构、与其他表的关联条件以及查询条件等。
对机密数据提供安全保护:有了视图,就可以在设计数据库应用系统时,对不同的用户定义不同的视图,避免机密数据(如,敏感字段“salary”)出现在不应该看到这些数据的用户视图上。这样视图就自动提供了对机密数据的安全保护功能
实例一:单表视图
创建/替换单表视图:
1
2
3
4
5
6-- 创建/替换单表视图:
create or replace view myview01
as
select empno,ename,job,deptno
from emp
where deptno = 20查看视图:
1
2-- 查看视图:
select * from myview01;向视图中插入满足条件的数据
1
2-- 在视图中插入数据:
insert into myview01 (empno,ename,job,deptno) values (9999,'lili','CLERK',20);查看视图:
向视图中插入不满足条件的数据
1
insert into myview01 (empno,ename,job,deptno) values (8888,'nana','CLERK',30);
查看原来的表,发现成功插进去了
解决上面问题:
需要加上check option
1
2
3
4
5
6
7-- 创建/替换单表视图:
create or replace view myview01
as
select empno,ename,job,deptno
from emp
where deptno = 20
with check option;这样不满足条件的数据就不会轻易被加进去:
实例二:多表视图
1 | -- 创建/替换多表视图: |
实例三:统计视图
1 | -- 创建统计视图: |
实例四:基于视图的视图
1 | -- 创建基于视图的视图: |
3. 存储过程
概念
通过前面章节的学习,我们已经知道SQL是一种非常便利的语言。从数据库抽取数据,或者对特定的数据集中更新时,都能通过简洁直观的代码实现。
但是这个所谓的“简洁”也是有限制,SQL基本是一个命令实现一个处理,是所谓的非程序语言。
在不能编写流程的情况下,所有的处理只能通过一个个命令来实现。当然,通过使用连接及子查询,即使使用SQL的单一命令也能实现一些高级的处理,但是,其局限性是显而易见的。例如,在SQL中就很难实现针对不同条件进行不同的处理以及循环等功能。
这个时候就出现了存储过程这个概念,简单地说,存储过程就是数据库中保存(Stored)的一系列SQL命令(Procedure)的集合。也可以将其看作相互之间有关系的SQL命令组织在一起形成的一个小程序。
优点
- 提高执行性能。存储过程执行效率之所高,在于普通的SQL语句,每次都会对语法分析,编译,执行,而存储过程只是在第一次执行语法分析,编译,执行,以后都是对结果进行调用。
- 可减轻网络负担。使用存储过程,复杂的数据库操作也可以在数据库服务器中完成。只需要从客户端(或应用程序)传递给数据库必要的参数就行,比起需要多次传递SQL命令本身,这大大减轻了网络负担。
- 可将数据库的处理黑匣子化。应用程序中完全不用考虑存储过程的内部详细处理,只需要知道调用哪个存储过程就可以了
实例
定义一个没有返回值的存储过程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16-- 定义一个没有返回值 存储过程
-- 实现:模糊查询操作:
select * from emp where ename like '%A%';
create procedure mypro01(name varchar(10))
begin
if name is null or name = "" then
select * from emp; -- 传入null,就查看全表
else
select * from emp where ename like concat('%',name,'%');
end if;
end;
-- 删除存储过程:
drop procedure mypro01;
-- 调用存储过程:
call mypro01(null);
call mypro01('R');结果:
定义一个有返回值的存储过程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19-- 定义一个有返回值的存储过程
-- in 参数前面的in可以不写
-- out
-- found_rows() 是mysql定义的一个函数,作用返回查询结果的条数
-- into
create procedure mypro02 (in name varchar(10),out num int(4))
begin
if name is null or name = "" then
select * from emp;
else
select * from emp where name like concat('%',name,'%');
end if;
select found_rows() into num;
end;
-- 调用存储过程
call mypro02(null,@num);
select @num;
call mypro02('A',@aaa); -- 传入的参数名不一定要一样
select @aaa;结果: