1. 初识Mysql数据库
1.1 数据库的相关概念
数据库(database)顾名思义是用来组织、存储和管理数据的仓库。为了方便管理互联网世界中的数据,就有了数据库管理系统的概念(简称:数据库)。用户可以对数据库中的数据进行新增、查询、更新、删除等操作。
目前主流的关系型数据库管理系统的2024市场占有率排名如下:
- Oracle:大型的收费数据库,Oracle公司产品,价格昂贵。
- MySQL:开源免费的中小型数据库,后来Sun公司收购了MySQL,而Oracle又收购了Sun公司。 目前Oracle推出了收费版本的MySQL,也提供了免费的社区版本。(大多数公司使用的主流数据库)
- SQL Server:Microsoft 公司推出的收费的中型数据库,C#、.net等语言常用。
- PostgreSQL:开源免费的中小型数据库
不论我们使用的是上面的哪一个关系型数据库,在操作时,使用SQL语言的语法是一样的。所以学习了MySQL, 基本上等于学会了操作其它关系型数据库。章哥就是从SQL Server 到 Mysql的无缝衔接
2.2 Mysql中的数据类型
对于大多数初级开发者来说很容易忽略对数据库基本数据类型的系统学习,直接学习SQL,而数据库的数据类型是在设计数据库时最基础并且重要的知识。章哥把这部分放在前面的主要原因是想让大家每次在学习或者回顾数据库知识的时,首先对数据库基本数据类型做一次记忆,就像在学习编程语言时先学习基本数据类型一样。
数值
数据类型 | 描述 | 大小 |
---|---|---|
tinyint | 十分小的数据 | 1个字节 |
smallint | 较小的数据 | 2个字节 |
mediumint | 中等大小的数据 | 3个字节 |
int | 标准的整数 | 4个字节 |
bigint | 较大的数据 | 8个字节 |
float | 浮点数 | 4个字节 |
double | 浮点数 | 8个字节 |
decimal | 字符串形式的浮点数,一般用于金融计算 |
字符串
数据类型 | 描述 | 大小 |
---|---|---|
char | 字符串固定大小 | 0~255 |
varchar | 可变字符串 | 0~65535 |
tinytext | 微型文本 | 2^8-1 |
text | 文本串 | 2^16-1 |
时间日期
数据类型 | 描述 | 格式 |
---|---|---|
date | 日期格式 | YYYY-MM-DD |
time | 时间格式 | HH:mm:ss |
datetime | 最常用的时间格式 | YYYY-MM-DD HH:mm:ss |
timestamp | 时间戳,1970.1.1到现在的毫秒数 | |
year | 年份表示 |
NULL
- 没有值,未知。(不要使用NULL值进行计算或统计)
2. 操作数据库SQL
概念:SQL全称 Structured Query Language,结构化查询语言。操作关系型数据库的编程语言,定义了 一套操作关系型数据库统一标准 。SQL语句,根据其功能,主要分为四类:DDL、DML、DQL、DCL。
2.1 SQL通用语法
在学习具体的SQL语句之前,先来了解一下SQL语言的同于语法。
- SQL语句可以单行或多行书写,以分号结尾。
- SQL语句可以使用空格/缩进来增强语句的可读性。
- MySQL数据库的SQL语句不区分大小写,关键字建议使用大写。
- 注释: 单行注释:-- 注释内容 或 # 注释内容,多行注释:/* 注释内容 */
2.2 DDL
Data Definition Language:数据库定义语言,用来定义数据库对象(数据库,表,字段)
2.2.1 数据库操作
- 查询所有数据库:show databases ;
- 查询当前数据库:select database()
- 创建数据库:create database [ if not exists ] 数据库名 [ default charset 字符集 ] [ collate 排序 规则 ] ;
- 删除数据库: drop database [ if exists ] 数据库名 ;
- 切换数据库:use 数据库名 ;
注意事项:
在创建多个数据库时如果要指定 字符集 和 排序规则 ,一定要设置相同的字符集和排序规则。
2.2.2 表操作
表操作-查询创建
- 查询当前数据库所有表:show tables;
- 查看指定表结构:desc 表名 ;
- 查询指定表的建表语句: show create table 表名
- 创建表结构
表操作-修改
- 添加字段:ALTER TABLE 表名 ADD 字段名 类型 (长度) [ COMMENT 注释 ] [ 约束 ];
- 修改数据类型:ALTER TABLE 表名 MODIFY 字段名 新数据类型 (长度);
- 修改字段名和字段类型:ALTER TABLE 表名 CHANGE 旧字段名 新字段名 类型 (长度) [ COMMENT 注释 ] [ 约束 ];
- 删除字段:ALTER TABLE 表名 DROP 字段名;
- 修改表名:ALTER TABLE 表名 RENAME TO 新表名;
-- 修改
-- 1、为emp表增加一个新的字段”昵称”为nickname,类型为varchar(20)
ALTER TABLE emp ADD nickname varchar(20) COMMENT '昵称';
-- 2、将emp表的nickname字段修改为username,类型为varchar(30)
ALTER TABLE emp CHANGE nickname username varchar(30) COMMENT '昵称';
-- 将emp表的字段username删除
ALTER TABLE emp DROP username;
-- 修改表名
ALTER TABLE 表名 RENAME TO 新表名;
表操作-删除
- 删除表:DROP TABLE [ IF EXISTS ] 表名;
- 删除指定表,并重新创建表:TRUNCATE TABLE 表名;
-- 如果tb_user表存在,则删除tb_user表
DROP TABLE IF EXISTS tb_user;
-- 删除指定表, 并重新创建表
TRUNCATE TABLE 表名;
注意: 在删除表的时候,表中的全部数据也都会被删除。
2.3 DML
Data Manipulation Language:数据操作语言,用来对数据库中表的数据记录进 行增、删、改操作
- 添加数据(INSERT)
- 修改数据(UPDATE)
- 删除数据(DELETE)
添加数据
- 给指定字段添加数据:INSERT INTO 表名 (字段名1, 字段名2, ...) VALUES (值1, 值2, ...);
- 给全部字段添加数据:INSERT INTO 表名 VALUES (值1, 值2, ...);
- 批量添加数据:
- INSERT INTO 表名 (字段名1, 字段名2, ...) VALUES (值1, 值2, ...), (值1, 值2, ...), (值 1, 值2, ...) ;
- INSERT INTO 表名 VALUES (值1, 值2, ...), (值1, 值2, ...), (值1, 值2, ...) ;
-- 1、给employee表指定的字段添加数据
insert into employee(id,workno,name,gender,age,idcard,entrydate) values(1,'1','Itcast','男',10,'123456789012345678','2000-01-01');
-- 2、给employee表所有的字段添加数据
insert into employee values(2,'2','张无忌','男',18,'123456789012345670','2005-01
01');
-- 3、批量插入数据到employee表
insert into employee values(3,'3','韦一笑','男',38,'123456789012345670','2005-01
01'),(4,'4','赵敏','女',18,'123456789012345670','2005-01-01');
修改数据
- 语法:UPDATE 表名 SET 字段名1 = 值1 , 字段名2 = 值2 , .... [ WHERE 条件 ] ;
-- 修改id为1的数据, 将name修改为小昭, gender修改为 女
update employee set name = '小昭' , gender = '女' where id = 1;
-- 将所有的员工入职日期修改为 2008-01-01
update employee set entrydate = '2008-01-01';
删除数据
- 语句:DELETE FROM 表名 [ WHERE 条件 ] ;
-- 删除gender为女的员工
delete from employee where gender = '女';
-- 删除所有员工
delete from employee;
注意:修改和删除语句的条件可以有,也可以没有,如果没有条件,则会修改或删除整张表的所有数据。
2.4 DQL(重点)
Data Query Language:数据查询语言,数据查询语言,用来查询数据库中表的记录。
查询关键字: SELECT
2.4.1 基本语法
DQL 查询语句,语法结构:
SELECT
字段列表
FROM
表名列表
WHERE
条件列表
GROUP BY
分组字段列表
HAVING
分组后条件列表
ORDER BY
排序字段列表
LIMIT
分页参数
根据查询类型,又可以分为以下几种:
- 基本查询(不带任何条件)
- 条件查询(WHERE) 聚合函数(count、max、min、avg、sum)
- 分组查询(group by)
- 排序查询(order by)
- 分页查询(limit)
2.4.2 基础查询
(1)查询多个字段
- SELECT 字段1, 字段2, 字段3 ... FROM 表名 ;
- 1SELECT * FROM 表名 ;
(2)字段设置别名
- SELECT 字段1 [ AS 别名1 ] , 字段2 [ AS 别名2 ] ... FROM 表名;
- SELECT 字段1 [ 别名1 ] , 字段2 [ 别名2 ] ... FROM 表名;
(3)去除重复记录
- SELECT DISTINCT 字段列表 FROM 表名;
案例:
-- 1、查询指定字段 name, workno, age并返回
select name,workno,age from emp;
-- 2、查询返回所有字段
select * from emp;
-- 3、查询所有员工的工作地址,起别名(as可省略)
select workaddress as '工作地址' from emp;
-- 4、查询公司员工的上班地址有哪些(不要重复)
select distinct workaddress '工作地址' from emp;
2.4.3 条件查询
常见的运算符如下:
操作符 | 含义 |
---|---|
= | 等于 |
<>或!= | 不等于 |
> | 大于 |
< | 小于 |
<= | 小于等于 |
>= | 大于等于 |
BETWEEN…AND… | 闭合区间 |
AND | 和 |
IS NULL | 是 NULL |
IN (...) | 在in之后的列表中的值,多选一 |
OR 或 || | 或者 (多个条件任意一个成立) |
AND 或 && | 并且 (多个条件同时成立) |
NOT 或 ! | 非 , 不是 |
-- 1、查询年龄在15岁(包含) 到 20岁(包含)之间的员工信息
select * from emp where age >= 15 && age <= 20;
select * from emp where age >= 15 and age <= 20;
select * from emp where age between 15 and 20;
-- 2、查询没有身份证号的员工信息
select * from emp where idcard is null;
-- 3、查询姓名为两个字的员工信息
select * from emp where name like '__';
-- 4、查询身份证号最后一位是X的员工信息
select * from emp where idcard like '%X';
2.4.4 聚合函数
常见的聚合函数
函数 | 功能 |
---|---|
count | 统计数量 |
max | 最大值 |
min | 最小值 |
avg | 平均值 |
sum | 求和 |
- 语法:SELECT 聚合函数(字段列表) FROM 表名 ;
案例:
-- 统计该企业员工数量
select count(*) from emp; -- 统计的是总记录数
select count(idcard) from emp; -- 统计的是idcard字段不为null的记录数
-- 统计西安地区员工的年龄之和
select sum(age) from emp where workaddress = '西安';
2.4.5 分组查询
- 语法:SELECT 字段列表 FROM 表名 [ WHERE 条件 ] GROUP BY 分组字段名 [ HAVING 分组后过滤条件 ];
-- 根据性别分组 , 统计男性员工 和 女性员工的数量
select gender, count(*) from emp group by gender ;
-- 查询年龄小于45的员工 , 并根据工作地址分组 , 获取员工数量大于等于3的工作地址
select workaddress, count(*) address_count from emp where age < 45 group by
workaddress having address_count >= 3;
-- 统计各个工作地址上班的男性及女性员工的数量
select workaddress, gender, count(*) '数量' from emp group by gender , workaddress ;
where与having区别 执行时机不同:
- where是分组之前进行过滤,不满足where条件,不参与分组;而having是分组之后对结果进行过滤。
- 判断条件不同:where不能对聚合函数进行判断,而having可以。
注意事项:
- 分组之后,查询的字段一般为聚合函数和分组字段,查询其他字段无任何意义。(Mysql 5.7.5版本在sql_mode中增加了ONLY_FULL_GROUP_BY参数用来开启或者关闭针对group by的限制。默认强制查询字段必须是聚合函数或分组的字段)
- 执行顺序: where > 聚合函数 > having 。
- 支持多字段分组, 具体语法为 : group by columnA,columnB
2.4.6 排序查询
- 语法:SELECT 字段列表 FROM 表名 ORDER BY 字段1 排序方式1 , 字段2 排序方式2 ;
-- 根据年龄对公司的员工进行升序排序 , 年龄相同 , 再按照入职时间进行降序排序
select * from emp order by age asc , entrydate desc;
2.4.7 查询分页
- 语法:SELECT 字段列表 FROM 表名 LIMIT 起始索引, 查询记录数 ;
-- 查询第1页员工数据, 每页展示10条记录
select * from emp limit 0,10;
select * from emp limit 10;
-- 查询第2页员工数据, 每页展示10条记录
select * from emp limit 10,10;
注意事项:
- 起始索引从0开始,起始索引 = (查询页码 - 1)* 每页显示记录数。
- 分页查询是数据库的方言,不同的数据库有不同的实现,MySQL中是LIMIT。
- 如果查询的是第一页数据,起始索引可以省略,直接简写为 limit 10。
2.5 DCL
Data Control Language:数据控制语言,用来管理数据库用户、控制数据库的访问权限。
2.5.1 管理用户
- 查询用户:select * from mysql.user;
其中 Host代表当前用户访问的主机, 如果为localhost, 仅代表只能够在当前本机访问,是不可以远程访问的。 User代表的是访问该数据库的用户名。在MySQL中需要通过Host和User来唯一标识一 个用户。
- 创建用户:CREATE USER '用户名'@'主机名' IDENTIFIED BY '密码';
- 修改用户密码:ALTER USER '用户名'@'主机名' IDENTIFIED WITH mysql_native_password BY '新密码' ;
- 删除用户:DROP USER '用户名'@'主机名' ;
-- 创建用户zhangsan, 只能够在当前主机localhost访问, 密码123456
create user 'zhangsan'@'localhost' identified by '123456';
-- 创建用户zhangsan, 可以在任意主机访问该数据库, 密码123456
create user 'zhangsan'@'%' identified by '123456';
-- 修改用户zhangsan的访问密码为1234
alter user 'zhangsan'@'%' identified with mysql_native_password by '1234';
-- 删除 zhangsan@localhost 用户
drop user 'zhangsan'@'localhost';
注意事项:
- 在MySQL中需要通过用户名@主机名的方式,来唯一标识一个用户。
- 主机名可以使用 % 通配。
- 这类SQL开发人员操作的比较少,主要是DBA( Database Administrator 数据库管理员)使用。,对这部分有个大致了解就行。
2.5.2 权限控制
MySQL中定义了很多种权限,但是常用的就以下几种:
权限 | 说明 |
---|---|
ALL, ALL PRIVILEGES | 所有权限 |
SELECT | 查询数据 |
INSERT | 插入数据 |
UPDATE | 修改数据 |
DELETE | 删除数据 |
ALTER | 修改表 |
DROP | 删除数据库/表/视图 |
CREATE | 创建数据库/表 |
- 查询权限:SHOW GRANTS FOR '用户名'@'主机名' ;
- 授予权限:GRANT 权限列表 ON 数据库名.表名 TO '用户名'@'主机名';
- 撤销权限:REVOKE 权限列表 ON 数据库名.表名 FROM '用户名'@'主机名';
3. 函数
字符串函数
函数 | 功能 |
---|---|
CONCAT(S1,S2,...Sn) | 字符串拼接,将S1,S2,... Sn拼接成一个字符串 |
LOWER(str) | 将字符串str全部转为小写 |
UPPER(str) | 将字符串str全部转为大写 |
LPAD(str,n,pad) | 左填充,用字符串pad对str的左边进行填充,达到n个字符 串长度 |
RPAD(str,n,pad) | 右填充,用字符串pad对str的右边进行填充,达到n个字符 串长度 |
TRIM(str) | 去掉字符串头部和尾部的空格 |
SUBSTRING(str,start,len) | 返回从字符串str从start位置起的len个长度的字符串 |
案例:
-- 企业员工的工号,统一为5位数,目前不足5位数的全部在前面补0。比如: 1号员
工的工号应该为00001
update emp set workno = lpad(workno, 5, '0');
数值函数
函数 | 功能 |
---|---|
CEIL(x) | 向上取整 |
FLOOR(x) | 向下取整 |
MOD(x,y) | 返回x/y的模 |
RAND() | 返回0~1内的随机数 |
ROUND(x,y) | 求参数x的四舍五入的值,保留y位小数 |
案例:
-- 获取随机数可以通过rand()函数,但是获取出来的随机数是在0-1之间的,所以可以在其基础
上乘以1000000,然后舍弃小数部分,如果长度不足6位,补0
select lpad(round(rand()*1000000 , 0), 6, '0');
日期函数
函数 | 功能 |
---|---|
CURDATE() | 返回当前日期 |
CURTIME() | 返回当前时间 |
NOW() | 返回当前日期和时间 |
YEAR(date) | 获取指定date的年份 |
MONTH(date) | 获取指定date的月份 |
DAY(date) | 获取指定date的日期 |
DATE_ADD(date, INTERVAL expr type) | 返回一个日期/时间值加上一个时间间隔expr后的时间值 |
DATEDIFF(date1,date2) | 返回起始时间date1 和 结束时间date2之间的天数 |
案例:
-- 查询所有员工的入职天数,并根据入职天数倒序排序
select name, datediff(curdate(), entrydate) as 'entrydays' from emp order by
entrydays desc;
流程函数
函数 | 功能 |
---|---|
IF(value , t , f) | 如果value为true,则返回t,否则返回 f |
IFNULL(value1 , value2) | 如果value1不为空,返回value1,否则 返回value2 |
CASE WHEN [ val1 ] THEN [res1] ... ELSE [ default ] END | 如果val1为true,返回res1,... 否 则返回default默认值 |
CASE [ expr ] WHEN [ val1 ] THEN [res1] ... ELSE [ default ] END | 如果expr的值等于val1,返回 res1,... 否则返回default默认值 |
案例:
-- 查询emp表的员工姓名和工作地址 (北京/上海 ----> 一线城市 , 其他 ----> 二线城市)
select
name,
( case workaddress when '北京' then '一线城市' when '上海' then '一线城市' else
'二线城市' end ) as '工作地址'
from emp;
4. 约束
概念:约束是作用于表中字段上的规则,用于限制存储在表中的数据。
目的:保证数据库中数据的正确、有效性和完整性。
字段约束:在创建表是对字段进行约束
关键字 | 约束 | 描述 |
NOT NULL | 非空约束 | 限制该字段的数据不能为null |
UNIQUE | 唯一约束 | 保证该字段的所有数据都是唯一、不重复的 |
PRIMARY KEY | 主键约束 | 主键是一行数据的唯一标识,要求非空且唯一 |
DEFAULT | 默认约束 | 保存数据时,如果未指定该字段的值,则采用默认值 |
CHECK | 检查约束(8.0.16版本 之后) | 保证字段值满足某一个条件 |
FOREIGN KEY | 外键约束 | 用来让两张表的数据之间建立连接,保证数据的一致 性和完整性 |
外键约束:创建表与表之间的联系
为emp表的dept_id字段添加外键约束,关联dept表的主键id。
alter table emp add constraint fk_emp_dept_id foreign key (dept_id) references
dept(id);
左侧的emp表是员工表,里面存储员工的基本信息,包含员工的ID、姓名、年龄、职位、薪资、入职日 期、上级主管ID、部门ID,在员工的信息中存储的是部门的ID dept_id,而这个部门的ID是关联的 部门表dept的主键id,那emp表的dept_id就是外键,关联的是另一张表的主键。
- 删除emp表的外键fk_emp_dept_id
alter table emp drop foreign key fk_emp_dept_id;
注意:
一般在设计数据库时一般不会真的设置外键,而只加表与表关联的外键字段。因为设置外键会影响性能并增加数据库架构的复杂性,一般用业务逻辑约束数据库增删改操作。
5. 多表查询(重中之重)
在项目业务中查询操作的频次是要远高于增删改,而多表查询是业务查询的重中之重,一般需要根据业务需求联查4、5个表甚至更多。所以充分理解并掌握多表查询非常重要
5.1 多表关系
表与表的关系基本上分为三种:
- 一对多(多对一)
- 多对多
- 一对一
一对多
- 案例: 部门 与 员工的关系
- 关系: 一个部门对应多个员工,一个员工对应一个部门
- 实现: 在多的一方建立外键,指向一的一方的主键
多对多
- 案例: 学生 与 课程的关系
- 关系: 一个学生可以选修多门课程,一门课程也可以供多个学生选择
- 实现: 建立第三张中间表,中间表至少包含两个外键,分别关联两方主键
一对一
- 案例: 用户 与 用户详情的关系
- 关系: 一对一关系,多用于单表拆分,将一张表的基础字段放在一张表中,其他详情字段放在另 一张表中,以提升操作效率
- 实现: 在任意一方加入外键,关联另外一方的主键,并且设置外键为唯一的(UNIQUE)
5.2 子查询
SQL语句中嵌套SELECT语句,称为嵌套查询,又称子查询。子查询外部的语句可以是INSERT / UPDATE / DELETE / SELECT 的任何一个。
根据子查询结果又分为:
- 标量子查询
子查询返回的结果是单个值(数字、字符串、日期等),最简单的形式,称为标量子查询。
案例:
-- 查询指定入职日期之后入职的员工信息
select * from emp where entrydate > (select entrydate from emp where name = '方东
白');
- 列子查询
子查询返回的结果是一列(可以是多行),称为列子查询
案例:
-- 查询 "销售部" 和 "市场部" 的所有员工信息
select * from emp where dept_id in (select id from dept where name = '销售部' or
name = '市场部');
- 行子查询
子查询返回的结果是一行(可以是多列),这种子查询称为行子查询
案例:
-- 查询与 "张无忌" 的薪资及直属领导相同的员工信息
select salary, managerid from emp where name = '张无忌';
- 表子查询
子查询返回的结果是多行多列,这种子查询称为表子查询
案例:
-- 查询与 "鹿杖客" , "宋远桥" 的职位和薪资相同的员工信息
select * from emp where (job,salary) in ( select job, salary from emp where name =
'鹿杖客' or name = '宋远桥' );
5.3 联合查询
案例:
-- 内连接
-- 1、查询每一个员工的姓名 , 及关联的部门的名称 (隐式内连接实现)
select e.name,d.name from emp e , dept d where e.dept_id = d.id;
-- 2、查询每一个员工的姓名 , 及关联的部门的名称 (显式内连接实现)
select e.name, d.name from emp e join dept d on e.dept_id = d.id;
-- 外连接
-- 1、查询emp表的所有数据, 和对应的部门信息
select e.*, d.name from emp e left outer join dept d on e.dept_id = d.id;
select e.*, d.name from emp e left join dept d on e.dept_id = d.id;
-- 2、查询dept表的所有数据, 和对应的员工信息(右外连接)
select d.*, e.* from emp e right outer join dept d on e.dept_id = d.id;
select d.*, e.* from dept d left outer join emp e on e.dept_id = d.id;
注意事项:
左外连接和右外连接是可以相互替换的,只需要调整在连接查询时SQL中,表结构的先后顺 序就可以了。而我们在日常开发使用时,更偏向于左外连接,因为一般人的思维都是从左到右,所以将左表作为主表。
5.4 自连接
5.4.1 自连接查询
自连接查询,顾名思义,就是自己连接自己,也就是把一张表连接查询多次。
自连接的查询语法:
SELECT 字段列表 FROM 表A 别名A JOIN 表A 别名B ON 条件 ... ;
-- 查询员工 及其 所属领导的名字
select a.name , b.name from emp a , emp b where a.managerid = b.id;
-- 查询所有员工 emp 及其领导的名字 emp , 如果员工没有领导, 也需要查询出来
表结构: emp a , emp b
select a.name '员工', b.name '领导' from emp a left join emp b on a.managerid =
b.id;
5.4.2 联合查询
对于union查询,就是把多次查询的结果合并起来,形成一个新的查询结果集。
SELECT 字段列表 FROM 表A ...
UNION [ ALL ]
SELECT 字段列表 FROM 表B ....;
- 对于联合查询的多张表的列数必须保持一致,字段类型也需要保持一致。
- union all 会将全部的数据直接合并在一起,union 会对合并之后的数据去重。
-- 将薪资低于 5000 的员工 , 和 年龄大于 50 岁的员工全部查询出来
select * from emp where salary < 5000
union all
select * from emp where age > 50;
6. 事务
概念:事务简单理解是一组操作(SQL语句执行)的集合,它是一个不可分割的工作单位,事务会把所有的操作作为一个整体一起向系统提交或撤销操作请求,保证这些操作要么同时成功,要么同时失败。
比如:A向B转钱业务,A扣钱和B增钱同时成功或失败。
6.1 控制事务
-- 开启事务
start transaction -- 或 BEGIN 开启
-- 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.2 事务的四大特性(ACID)
- 原子性(Atomicity):事务是不可分割的最小操作单元,要么全部成功,要么全部失败。
- 一致性(Consistency):事务完成时,必须使所有的数据都保持一致状态。
- 隔离性(Isolation):数据库系统提供的隔离机制,保证事务在不受外部并发操作影响的独立 环境下运行。
- 持久性(Durability):事务一旦提交或回滚,它对数据库中的数据的改变就是永久的。
6.3 并发事务问题
- (1)赃读:一个事务读到另外一个事务还没有提交的数据
- (2)幻读:一个事务按照条件查询数据时,另一个事务并发插入数据,随后查询发现多出一些数据
- (3)丢失修改:两个事务同时修改一个数据,第一次的修改操作无效。(A=20,事务1、2并发执行A = A-1,结果A=19)
- (4)不可重复读:一个事务先后读取同一条记录,期间并发修改该数据,两次读取的数据不同,称之为不可重复读。
6.4 Mysql事务的隔离级别
- Read Uncommitted(读未提交内容):在该隔离级别,所有事务都可以看到其他未提交事务的执行结果。本隔离级别很少用于实际应用,因为它的性能也不比其他级别好多少,而且会造成脏读(读取到未提交的数据,而后该数据回滚)
- Read Committed(读已提交内容):这是大多数数据库系统的默认隔离级别(但不是MySQL默认的)。它满足了隔离的简单定义:一个事务只能看见已经提交事务所做的改变。这种隔离级别存在不可重复读现象,因为同一事务的其他实例在该实例处理其间可能会有新的commit,所以同一select可能返回不同结果。
- Repeatable Read(可重复读,默认):InnoDB和Falcon存储引擎通过多版本并发控制机制(MVCC),每个事务读取数据库数据会看到一个一致的数据快照,不受其他事务影响。当一个事务修改数据会生成新的数据库版本,保留旧版本。其他事务读取数据会根据隔离级别和版本访问数据。(不能完全解决幻读)
- Serializable(串行化):这是最高的隔离级别,它通过强制事务排序依次执行,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每个读的数据行上加上共享锁。在这个级别,可能导致大量的超时现象和锁竞争。(性能极低)
注意:事务我们一般不用SQL实现,但事务的特性、并发问题和隔离级别一直是面试常问点