MySQL基础:SQL分类DDL、DML、DQL、DCL;函数、约束、多表查询、事务、并发事务四大问题、事务隔离级别——脏写、脏读、不可重复读、幻读

一、前言

mysql与mysqld的区别:

  • mysqld 是一个守护进程,MySQL数据库系统的后台服务程序,也称为mysql服务器,是一个服务。在Linux中,服务通常以“d”结尾,这里的“d”代表“daemon”,意为守护进程。

    mysqld负责监听客户端的连接请求,处理SQL查询,管理数据库文件,以及与数据库相关的其他任务。

    当你运行MySQL服务器时,实际上是在启动mysqld进程。

  • mysql 是一个交互式输入SQL语句或从SQL文件批处理它们的命令行工具,即 MySQL 命令行客户端,它相当于一个客户端软件,可以对服务端的mysqld发起连接

还记得MySQL启动关闭连接常用命令吗?如果忘记的话,可以到这里重温复习MySQL+Redis+PostgreSQL+ClickHouse 启动关闭连接常用命令

二、SQL分类:DDL、DML、DQL、DCL

SQL通用语法

在学习具体的SQL语句之前,先来了解一下SQL语言的通用语法。

1)SQL语句可以单行或多行书写,以分号结尾。

2)SQL语句可以使用空格/缩进来增强语句的可读性。

3)MySQL数据库的SQL语句不区分大小写,关键字建议使用大写。

4)注释:

  • 单行注释:-- 注释内容 或 # 注释内容
  • 多行注释:/* 注释内容 */

SQL分类:SQL语句,根据其功能,主要分为四类:DDL、DML、DQL、DCL。

分类全称说明
DDLData Definition Language数据定义语言,用来定义数据库对象(数据库,表, 字段)
DMLData Manipulation Language数据操作语言,用来对数据库表中的数据进行增删改
DQLData Query Language数据查询语言,用来查询数据库中表的记录
DCLData Control Language数据控制语言,用来创建数据库用户、控制数据库的访问权限

2.1 DDL

Data Definition Language,数据定义语言,用来定义数据库对象(数据库,表,字段) 。

2.1.1 数据库操作

查询所有数据库、切换数据库、查询当前数据库、创建数据库、删除数据库、

show databases;      #查询所有数据库
USE 数据库名;         #切换到指定的数据库
select database();   #查看当前使用数据库

create database [ if not exists ] 数据库名 [ default charset 字符集 ] [ collate 排序规则 ] ;  #创建数据库
drop database [ if exists ] 数据库名 ;    #删除数据库

案例:

1)创建一个jw数据库,使用数据库默认的字符集。

create database jw;

在同一个数据库服务器中,不能创建两个名称相同的数据库,否则会报错。

可以通过if not exists 参数来解决这个问题,数据库不存在, 则创建该数据库,如果存在,则不创建。

在这里插入图片描述

2)创建一个jwtest数据库,并指定字符集。

create database jwtest default charset utf8mb4;

如果删除一个不存在的数据库,将会报错。此时,可以加上参数 if exists ,如果数据库存在,再执行删除,否则不执行删除。

在这里插入图片描述

2.2.2 表操作

2.2.2.1 表操作-查询创建

查询当前数据库所有表、查看指定表结构、查询指定表的建表语句、创建表结构、

###数据表
show tables;  #显示当前数据库的所有表,使用该命令前需要使用use命令来选择要操作的数据库
describe table_name;desc 表名; #表的详细描述,显示表结构及字段
#显示数据表的属性,属性类型,主键信息 ,是否为 NULL,默认值等其他信息
show columns from 数据表;
show create table 表名;   #查看该表的建表语句。有部分参数我们在创建表的时候,并未指定也会查询到,因为这部分是数据库的默认值,如:存储引擎、字符集等
show index from 数据表;   #显示数据表的详细索引信息,包括PRIMARY KEY(主键)


#创建表结构。[...] 内为可选参数,最后一个字段后面没有逗号
CREATE TABLE 表名(
 字段1 字段1类型 [ COMMENT 字段1注释 ],
 字段2 字段2类型 [COMMENT 字段2注释 ],
 字段3 字段3类型 [COMMENT 字段3注释 ],
 ......
 字段n 字段n类型 [COMMENT 字段n注释 ] 
) [ COMMENT 表注释 ] ;  

create table tb_user(
 id int comment '编号',
 name varchar(50) comment '姓名',
 age int comment '年龄',
 gender varchar(1) comment '性别'
) comment '用户表';


drop table tb_name;       #删除表
delete from tb_name;      #清空表

alter table 表名 add/modify/change/drop/rename to ...;  #修改表结构
2.2.2.2 表操作-数据类型

在上述的建表语句中,我们在指定字段的数据类型时,用到了int ,varchar,那么在MySQL中除了以上的数据类型,还有哪些常见的数据类型呢? 接下来,我们就来详细介绍一下MySQL的数据类型。

MySQL中的数据类型有很多,主要分为三类:数值类型、字符串类型、日期时间类型。

1)数值类型

类型大小有符号(SIGNED)范围无符号(UNSIGNED)范围描述
TINYINT1byte(-128,127)(0,255)小整数值
SMALLINT2bytes(-32768,32767)(0,65535)大整数值
MEDIUMINT3bytes(-8388608,8388607)(0,16777215)大整数值
INT/INTEGER4bytes(-2147483648, 2147483647)(0,4294967295)大整数值
BIGINT8bytes(-263,263-1)(0,2^64-1)极大整数值
FLOAT4bytes(-3.402823466 E+38, 3.402823466351 E+38)0 和 (1.175494351 E38,3.402823466 E+38)单精度浮点数值
DOUBLE8bytes(-1.7976931348623157 E+308, 1.7976931348623157 E+308)0 和 (2.2250738585072014 E-308, 1.7976931348623157 E+308)双精度浮点数值
DECIMAL依赖于M(精度)和D(标度) 的值依赖于M(精度)和D(标度)的 值小数值(精确定点数)
如: 
1). 年龄字段 -- 不会出现负数, 而且人的年龄不会太大
age tinyint unsigned

2). 分数 -- 总分100分, 最多出现一位小数
score double(4,1)

2)字符串类型

类型大小描述
CHAR0-255 bytes定长字符串(需要指定长度)
VARCHAR0-65535 bytes变长字符串(需要指定长度)
TINYBLOB0-255 bytes不超过255个字符的二进制数据
TINYTEXT0-255 bytes短文本字符串
BLOB0-65 535 bytes二进制形式的长文本数据
TEXT0-65 535 bytes长文本数据
MEDIUMBLOB0-16 777 215 bytes二进制形式的中等长度文本数据
MEDIUMTEXT0-16 777 215 bytes中等长度文本数据
LONGBLOB0-4 294 967 295 bytes二进制形式的极大文本数据
LONGTEXT0-4 294 967 295 bytes极大文本数据

char 与 varchar 都可以描述字符串,char是定长字符串,指定长度多长,就占用多少个字符,和字段值的长度无关 。而varchar是变长字符串,指定的长度为最大占用长度 。相对来说,char的性能会更高些。

如: 
1). 用户名 username ------> 长度不定, 最长不会超过50
username varchar(50)

2). 性别 gender ---------> 存储值, 不是男,就是女
gender char(1)

3). 手机号 phone --------> 固定长度为11
phone char(11)

注:char(n) 和 varchar(n) 中括号中 n 代表字符的个数,并不代表字节个数

3)日期时间类型

类型大小(字节)范围格式描述
DATE31000-01-01 至 9999-12-31YYYY-MM-DD日期值
TIME3-838:59:59 至 838:59:59HH:MM:SS时间值或持续 时间
YEAR11901 至 2155YYYY年份值
DATETIME81000-01-01 00:00:00 至 9999-12-31 23:59:59YYYY-MM-DD HH:MM:SS混合日期和时 间值
TIMESTAMP41970-01-01 00:00:01 至 2038-01-19 03:14:07YYYY-MM-DD HH:MM:SS混合日期和时 间值,时间戳
如: 
1). 生日字段 birthday
birthday date

2). 创建时间 createtime
createtime datetime
2.2.2.3 表操作-案例

设计一张员工信息表,要求如下:

  1. 编号(纯数字)
  2. 员工工号 (字符串类型,长度不超过10位)
  3. 员工姓名(字符串类型,长度不超过10位) 4
  4. 性别(男/女,存储一个汉字)
  5. 年龄(正常人年龄,不可能存储负数)
  6. 身份证号(二代身份证号均为18位,身份证中有X这样的字符)
  7. 入职时间(取值年月日即可)

对应的建表语句如下:

create table emp(
    id int comment '编号',
    workno varchar(10) comment '工号',
    name varchar(10) comment '姓名',
    gender char(1) comment '性别',
    age tinyint unsigned comment '年龄',
    idcard char(18) comment '身份证号',
    entrydate date comment '入职时间'
) comment '员工表';

SQL语句编写完毕之后,就可以在MySQL的命令行中执行SQL,然后也可以通过 desc 指令查询表结构信息。

表结构创建好了,里面的name字段是varchar类型,最大长度为10,也就意味着如果超过10将会报错,如果我们想修改这个字段的类型 或 修改字段的长度该如何操作呢?接下来再来讲解DDL语句中,如何操作表字段。

2.2.2.4 表操作-修改

添加字段、修改数据类型、修改字段名和字段类型、删除字段、修改表名

#添加字段
ALTER TABLE 表名 ADD 字段名 类型 (长度) [ COMMENT 注释 ] [ 约束 ];

#修改数据类型
ALTER TABLE 表名 MODIFY 字段名 新数据类型 (长度);

#修改字段名和字段类型
ALTER TABLE 表名 CHANGE 旧字段名 新字段名 类型 (长度) [ COMMENT 注释 ] [ 约束 ];

#删除字段
ALTER TABLE 表名 DROP 字段名;

# 修改表名
ALTER TABLE 表名 RENAME TO 新表名;

案例

#为emp表增加一个新的字段”昵称”为nickname,类型为varchar(20)
ALTER TABLE emp ADD nickname varchar(20) COMMENT '昵称';

#将emp表的nickname字段修改为username,类型为varchar(30)
ALTER TABLE emp CHANGE nickname username varchar(30) COMMENT '昵称';

#将emp表的字段username删除
ALTER TABLE emp DROP username;

#将emp表的表名修改为 employee
ALTER TABLE emp RENAME TO employee;
2.2.2.5 表操作-删除
DROP TABLE [ IF EXISTS ] 表名;      #删除表。可选项 IF EXISTS 代表,只有表名存在时才会删除该表,表名不存在,则不执行删除操作(如果不
加该参数项,删除一张不存在的表,执行将会报错)TRUNCATE TABLE 表名;                #删除指定表, 并重新创建表。注意: 在删除表的时候,表中的全部数据也都会被删除。

2.2 DML

DML英文全称是Data Manipulation Language(数据操作语言),用来对数据库中表的数据记录进 行增、删、改操作。

  • 添加数据(INSERT)
  • 修改数据(UPDATE)
  • 删除数据(DELETE)

2.2.1 添加数据

给指定字段添加数据、给全部字段添加数据、批量添加数据

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, ...) ;
insert into employee(id,workno,name,gender,age,idcard,entrydate) 
values(1,'1','Itcast','男',10,'123456789012345678','2000-01-01');
insert into employee values(2,'2','张无忌','男',18,'123456789012345670','2005-01-
01');
insert into employee values(3,'3','韦一笑','男',38,'123456789012345670','2005-01-01'),(4,'4','赵敏','女',18,'123456789012345670','2005-01-01');

注意事项:

  • 插入数据时,指定的字段顺序需要与值的顺序是一一对应的。
  • 字符串和日期型数据应该包含在引号中。
  • 插入的数据大小,应该在字段的规定范围内。

2.2.2 修改数据

UPDATE 表名 SET 字段名1 =1 , 字段名2 =2 , .... [ WHERE 条件 ] ;

注意事项: 修改语句的条件可以有,也可以没有,如果没有条件,则会修改整张表的所有数据。

案例

#修改id为1的数据,将name修改为itheima
update employee set name = 'itheima' where id = 1;

#修改id为1的数据, 将name修改为小昭, gender修改为 女
update employee set name = '小昭' , gender = '女' where id = 1;

#将所有的员工入职日期修改为 2008-01-01
update employee set entrydate = '2008-01-01';

2.2.3 删除数据

DELETE FROM 表名 [ WHERE 条件 ] ;

注意事项:

  • DELETE 语句的条件可以有,也可以没有,如果没有条件,则会删除整张表的所有数据。
  • DELETE 语句不能删除某一个字段的值(可以使用UPDATE,将该字段值置为NULL即可)。
  • 当进行删除全部数据操作时,datagrip会提示我们,询问是否确认删除,我们直接点击Execute即可。

2.3 DQL

DQL英文全称是Data Query Language(数据查询语言),数据查询语言,用来查询数据库中表的记录。

查询关键字: SELECT

在一个正常的业务系统中,查询操作的频次是要远高于增删改的,当我们去访问企业官网、电商网站,在这些网站中我们所看到的数据,实际都是需要从数据库中查询并展示的。而且在查询的过程中,可能还会涉及到条件、排序、分页等操作。

2.3.1 基本语法

DQL 查询语句,语法结构如下:

SELECT
	字段列表
FROM
	表名列表
WHERE
	条件列表
GROUP BY
	分组字段列表
HAVING
	分组后条件列表
ORDER BY
	排序字段列表
LIMIT
	分页参数

在这里插入图片描述

我们在讲解这部分内容的时候,会将上面的完整语法进行拆分,分为以下几个部分:

  • 基本查询(不带任何条件)
  • 条件查询(WHERE)
  • 聚合函数(count、max、min、avg、sum)
  • 分组查询(group by)
  • 排序查询(order by)
  • 分页查询(limit)

2.3.2 基础查询

在基本查询的DQL语句中,不带任何的查询条件,查询的语法如下:

#1.查询多个字段
SELECT 字段1, 字段2, 字段3 ... FROM 表名 ;
SELECT * FROM 表名 ;
#2.字段设置别名
SELECT 字段1 [ AS 别名1 ] , 字段2 [ AS 别名2 ] ... FROM 表名;
SELECT 字段1 [ 别名1 ] , 字段2 [ 别名2 ] ... FROM 表名;
#3.去除重复记录
SELECT DISTINCT 字段列表 FROM 表名;

注意:* 号代表查询所有字段,在实际开发中尽量少用(不直观、影响效率)。

案例:

#查询所有员工的工作地址,起别名
select workaddress as '工作地址' from emp;
-- as可以省略
select workaddress '工作地址' from emp;

#查询公司员工的上班地址有哪些(不要重复)
select distinct workaddress '工作地址' from emp;

2.3.3 条件查询

1)语法

SELECT 字段列表 FROM 表名 WHERE 条件列表 ;

2)条件

常用的比较运算符如下:

比较运算符功能
>大于
>=大于等于
<小于
<=小于等于
=等于
<> 或 !=不等于
BETWEEN … AND …在某个范围之内(含最小、最大值)
IN (…)在in之后的列表中的值,多选一
LIKE 占位符模糊匹配(_匹配单个字符, %匹配任意个字符)
IS NULL是NULL

常用的逻辑运算符如下:

逻辑运算符功能
AND 或 &&并且 (多个条件同时成立)
OR 或 ||或者 (多个条件任意一个成立)
NOT 或 !非 , 不是

案例:

#查询年龄小于 20 的员工信息
select * from emp where age < 20;
#查询没有身份证号的员工信息
select * from emp where idcard is null;
select * from emp where idcard is not null;    #有身份证号
#查询年龄不等于 88 的员工信息
select * from emp where age != 88;
select * from emp where age <> 88;
#查询年龄在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;
#查询性别为 女 且年龄小于 25岁的员工信息
select * from emp where gender = '女' and age < 25;
#查询年龄等于18 或 20 或 40 的员工信息
select * from emp where age = 18 or age = 20 or age =40;
select * from emp where age in(18,20,40);

#查询姓名为两个字的员工信息 _ %
select * from emp where name like '__';
#查询身份证号最后一位是X的员工信息
select * from emp where idcard like '%X';
select * from emp where idcard like '_________________X';

2.3.4 聚合函数

1)介绍:将一列数据作为一个整体,进行纵向计算 。

2)常见的聚合函数

函数功能
count统计数量
max最大值
min最小值
avg平均值
sum求和

3)语法

SELECT 聚合函数(字段列表) FROM 表名 ;

注意 : NULL值是不参与所有聚合函数运算的。

案例:

#统计该企业员工数量
select count(*) from emp; -- 统计的是总记录数
select count(idcard) from emp; -- 统计的是idcard字段不为null的记录数

对于count聚合函数,统计符合条件的总记录数,还可以通过 count(数字/字符串)的形式进行统计查询,比如:

select count(1) from emp;

对于count(*) 、count(字段)、 count(1) 的具体原理,我们在进阶篇中SQL优化部分会详细讲解,此处大家只需要知道如何使用即可。

#统计该企业员工的平均年龄
select avg(age) from emp;
#统计该企业员工的最大年龄
select max(age) from emp;
#统计该企业员工的最小年龄
select min(age) from emp;
#统计西安地区员工的年龄之和
select sum(age) from emp where workaddress = '西安';

2.3.5 分组查询

1)语法

SELECT 字段列表 FROM 表名 [ WHERE 条件 ] GROUP BY 分组字段名 [ HAVING 分组后过滤条件 ];

2)where与having区别

  • 执行时机不同:where是分组之前进行过滤,不满足where条件,不参与分组;而having是分组之后对结果进行过滤。
  • 判断条件不同:where不能对聚合函数进行判断,而having可以。

注意事项:

  • 分组之后,查询的字段一般为聚合函数和分组字段,查询其他字段无任何意义。
  • 执行顺序:where > 聚合函数 > having 。
  • 支持多字段分组, 具体语法为 : group by columnA,columnB

案例

#根据性别分组 , 统计男性员工 和 女性员工的数量
select gender, count(*) from emp group by gender ;
#根据性别分组 , 统计男性员工 和 女性员工的平均年龄
select gender, avg(age) 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 ;

2.3.6 排序查询

排序在日常开发中是非常常见的一个操作,有升序排序,也有降序排序。

1)语法

SELECT 字段列表 FROM 表名 ORDER BY 字段1 排序方式1 , 字段2 排序方式2 ;

2)排序方式

  • ASC:升序(默认值)
  • DESC:降序

注意事项:

  • 如果是升序, 可以不指定排序方式ASC ;
  • 如果是多字段排序,当第一个字段值相同时,才会根据第二个字段进行排序 ;

案例

#根据入职时间, 对员工进行降序排序
select * from emp order by entrydate desc;
#根据年龄对公司的员工进行升序排序 , 年龄相同 , 再按照入职时间进行降序排序
select * from emp order by age asc , entrydate desc;

2.3.7 分页查询

分页操作在业务系统开发时,也是非常常见的一个功能,我们在网站中看到的各种各样的分页条,后台都需要借助于数据库的分页操作。

1)语法

SELECT 字段列表 FROM 表名 LIMIT 起始索引, 查询记录数 ;

注意事项:

  • 起始索引从0开始,起始索引 = (查询页码 - 1)* 每页显示记录数。
  • 分页查询是数据库的方言,不同的数据库有不同的实现,MySQL中是LIMIT。
  • 如果查询的是第一页数据,起始索引可以省略,直接简写为 limit 10。

案例

#查询第1页员工数据, 每页展示10条记录
select * from emp limit 0,10;
select * from emp limit 10;
#查询第2页员工数据, 每页展示10条记录 --------> (页码-1)*页展示记录数
select * from emp limit 10,10;

2.3.8 案例

#查询年龄为20,21,22,23岁的女员工信息
select * from emp where gender = '女' and age in(20,21,22,23);
#查询性别为 男 ,并且年龄在 20-40 岁(含)以内的姓名为三个字的员工
select * from emp where gender = '男' and ( age between 20 and 40 ) and name like '___';
#统计员工表中, 年龄小于60岁的 , 男性员工和女性员工的人数
select gender, count(*) from emp where age < 60 group by gender;
#查询所有年龄小于等于35岁员工的姓名和年龄,并对查询结果按年龄升序排序,如果年龄相同按入职时间降序排序。
select name , age from emp where age <= 35 order by age asc , entrydate desc;
#查询性别为男,且年龄在20-40 岁(含)以内的前5个员工信息,对查询的结果按年龄升序排序,年龄相同按入职时间升序排序。
select * from emp where gender = '男' and age between 20 and 40 order by age asc , entrydate asc limit 5 ;

2.3.9 执行顺序

在讲解DQL语句的具体语法之前,我们已经讲解了DQL语句的完整语法,及编写顺序,接下来,我们要来说明的是DQL语句在执行时的执行顺序,也就是先执行那一部分,后执行那一部分。

在这里插入图片描述

可在from中设置别名,观测where、select是否能够使用该别名 从而验证DQL语句的执行顺序。【特殊:mysql中group by语句及其之后的语句可以使用select中的别名,因为mysql对其进行了扩充,其他数据库不支持】

验证

#查询年龄大于15的员工姓名、年龄,并根据年龄进行升序排序。
select name , age from emp where age > 15 order by age asc;

#在查询时,我们给emp表起一个别名 e,然后在select 及 where中使用该别名。
select e.name , e.age from emp e where e.age > 15 order by age asc;

执行上述SQL语句后,我们看到依然可以正常的查询到结果,此时就说明: from 先执行, 然后 where 和 select 执行。那 where 和 select 到底哪个先执行呢?

此时,此时我们可以给select后面的字段起别名,然后在 where 中使用这个别名,然后看看是否可以执行成功。

select e.name ename , e.age eage from emp e where eage > 15 order by age asc;

执行上述SQL报错了,由此我们可以得出结论: from 先执行,然后执行 where , 再执行select 。

select e.name ename , e.age eage from emp e where e.age > 15 order by eage asc;   #执行成功,证明 order by 是在select 语句之后执行的

综上所述,我们可以看到DQL语句的执行顺序为: from … where … group by … having … select … order by … limit …

2.4 DCL

DCL英文全称是Data Control Language(数据控制语言),用来管理数据库用户、控制数据库的访问权限。

在这里插入图片描述

2.4.1 管理用户

管理用户 这类SQL开发人员操作得比较少,主要DBA(Database Administrator 数据库管理员)使用

### 1.查询用户
select * from mysql.user;
# 或者
use mysql;
select * from user;

### 2.创建用户
create user '用户名'@'主机名' identified by '密码';
### 3.修改用户密码
alter user '用户名'@'主机名' identified with mysql_native_password by '新密码';
### 4.删除用户
drop user '用户名'@'主机名';

注意事项

  • 在MySQL中需要通过用户名@主机名的方式,来唯一标识一个用户。
  • 主机名可以使用 % 通配。
  • 这类SQL开发人员操作的比较少,主要是DBA( Database Administrator 数据库管理员)使用

在这里插入图片描述

案例:

#创建用户wj, 只能够在当前主机localhost访问, 密码123456
create user 'wj'@'localhost' identified by '123456';
#创建用户heima, 可以在任意主机访问该数据库, 密码123456
create user 'wj'@'%' identified by '123456';
#修改用户wj的访问密码为1234
alter user 'wj'@'%' identified with mysql_native_password by '1234';
#删除 wj@localhost 用户
drop user 'wj'@'localhost';
drop user 'wj'@'%';

2.4.2 权限控制

MySQL中定义了很多种权限,但是常用的就以下几种:

权限说明
ALL, ALL PRIVILEGES所有权限
SELECT查询数据
INSERT插入数据
UPDATE修改数据
DELETE删除数据
ALTER修改表
DROP删除数据库/表/视图
CREATE创建数据库/表

上述只是简单罗列了常见的几种权限描述,其他权限描述及含义,可以直接参考官方文档。

#1.查询权限
SHOW GRANTS FOR '用户名'@'主机名';
#2.授予权限
GRANT 权限列表 ON 数据库名.表名 TO '用户名'@'主机名';  # *.* 所有表
grant all on rdms.* to 'wj'@'%';
#3.撤销权限
REVOKE 权限列表 ON 数据库名.表名 FROM '用户名'@'主机名';

注意事项:

  • 多个权限之间,使用逗号分隔
  • 授权时, 数据库名和表名可以使用 * 进行通配,代表所有。

案例:

#查询 'wj'@'%' 用户的权限
show grants for 'wj'@'%';
#授予 'wj'@'%' 用户test数据库所有表的所有操作权限
grant all on test.* to 'wj'@'%';
#撤销 'wj'@'%' 用户的test数据库的所有权限
revoke all on test.* from 'wj'@'%';

三、函数

函数 是指一段可以直接被另一段程序调用的程序或代码。 也就意味着,这一段程序或代码在MySQL中已经给我们提供了,我们要做的就是在合适的业务场景调用对应的函数完成对应的业务需求即可。 那么,函数到底在哪儿使用呢?

在这里插入图片描述

MySQL中的函数主要分为以下四类: 字符串函数、数值函数、日期函数、流程函数。

3.1 字符串函数

MySQL内置了很多字符串函数,常用的几个如下:

函数功能
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个长度的字符串,索引值从1开始
select concat('Hello' , ' MySQL');
select lower('Hello');
select lpad('01', 5, '-');      # lpad 左填充
select rpad('01', 5, '-');      # rpad 右填充
select trim(' Hello MySQL ');   # trim 去除空格
select substring('Hello MySQL',1,5);    # substring 截取子字符串

3.2 数值函数

常见数值函数

函数功能
CEIL(x)向上取整
FLOOR(x)向下取整
MOD(x,y)返回x/y的模
RAND()返回0~1内的随机数
ROUND(x,y)求参数x的四舍五入的值,保留y位小数
select ceil(1.1);
select round(2.345,2);    //2.35     

案例:通过数据库的函数,生成一个六位数的随机验证码。

思路: 获取随机数可以通过rand()函数,但是获取出来的随机数是在0-1之间的,所以可以在其基础上乘以1000000,然后舍弃小数部分,如果长度不足6位,补0

select round(rand()×1000000,0);     #0.044×1000000=44000,1×1000000,不全,没有包含 5、15、115等数字
select lpad(round(rand()*1000000,0),6,'0'); 

3.3 日期函数

常见日期函数

函数功能
CURDATE()返回当前日期
CURTIME()返回当前时间
NOW()返回当前日期和时间
YEAR(date)返回指定date的年份,select YEAR(now())
MONTH(date)返回指定date的月份
DAY(date)返回指定date的日期
DATE_ADD(date,INTERVAL expr type)返回一个日期/时间值加上一个时间间隔expr后的时间值
DATEDIFF(date1,date2)返回起始时间date1和结束时间date2之间的天数,第1个时间 - 第2个时间
select YEAR(now());    #当前年
#在当前日期时间基础上 往后推70天
select date_add(now(), INTERVAL 70 DAY);
#查询所有员工的入职天数,并根据入职天数倒序排序
select name,datediff(curdate(), entrydate) as 'entrydays' from emp order by entrydays desc;

3.4 流程函数

常见流程函数 可在SQL语句中实现条件筛选,从而提高语句的效率。

函数功能
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默认值
select ifnull('Ok','Default');
select ifnull('','Default');

#查询emp表的员工姓名和工作地址(北京/上海---->一线城市,其他---->二线城市)
select 
	name,
	(case workaddress when '北京' then '一线城市' when '上海' then '一线城市' else '二线城市' end ) as '工作地址' 
from emp;

四、约束

4.1 概述

概念:约束是作用于表中字段上的规则,用于限制存储在表中的数据。

目的:保证数据库中数据的正确、有效性和完整性。

分类:

约束描述关键字
非空约束限制该字段的数据不能为nullNOT NULL
唯一约束保证该字段的所有数据都是唯一、不重复的UNIQUE
主键约束主键是一行数据的唯一标识,要求非空且唯一PRIMARY KEY
默认约束保存数据时,如果未指定该字段的值,则采用默认值DEFAULT
检查约束(8.0.16版本之后)保证字段值满足某一个条件CHECK
外键约束用来让两张表的数据之间建立连接,保证数据的一致性和完整性FOREIGN KEY

注意:约束是作用于表中字段上的,可以在创建表/修改表的时候添加约束。

4.2 约束演示

上面我们介绍了数据库中常见的约束,以及约束涉及到的关键字,那这些约束我们到底如何在创建表、修改表的时候来指定呢,接下来我们就通过一个案例,来演示一下。

案例需求: 根据需求,完成表结构的创建。需求如下:

在这里插入图片描述

对应的建表语句为:

CREATE TABLE tb_user(
    id int AUTO_INCREMENT PRIMARY KEY COMMENT 'ID唯一标识',
    name varchar(10) NOT NULL UNIQUE COMMENT '姓名' ,
    age int check (age > 0 && age <= 120) COMMENT '年龄' ,
    status char(1) default '1' COMMENT '状态',
    gender char(1) COMMENT '性别'
);

在为字段添加约束时,我们只需要在字段之后加上约束的关键字即可,需要关注其语法。

4.3 外键约束

4.3.1 介绍

外键:用来让两张表的数据之间建立连接,从而保证数据的一致性和完整性。

在这里插入图片描述

4.3.2 语法

1)添加外键 子表/父表

CREATE TABLE 表名(
    字段名 数据类型,
    ...
    [CONSTRAINT] [外键名称] FOREIGN KEY (外键字段名) REFERENCES 主表 (主表列名) 
);
ALTER TABLE 表名 ADD CONSTRAINT 外键名称 FOREIGN KEY (外键字段名) REFERENCES 主表 (主表列名) ;

案例: 为emp表的dept_id字段添加外键约束,关联dept表的主键id。

alter table emp add constraint fk_emp_dept_id foreign key (dept_id) references dept(id);

2)删除外键

ALTER TABLE 表名 DROP FOREIGN KEY 外键名称;

案例: 删除emp表的外键fk_emp_dept_id。

alter table emp drop foreign key fk_emp_dept_id;

4.3.3 删除/更新行为

添加了外键之后,在删除父表数据时产生的约束行为,我们就称为删除/更新行为。具体的删除/更新行为有以下几种:

行为说明
NO ACTION当在父表中删除/更新对应记录时,首先检查该记录是否有对应外键,如果有则不允许删除/更新。(与RESTRICE一致)默认行为
RESTRICT当在父表中删除/更新对应记录时,首先检查该记录是否有对应外键,如果有则不允许删除/更新。(与NO ACTION一致)默认行为
CASCADE当在父表中删除/更新对应记录时,首先检查该记录是否有对应外键,如果有,则也删除/更新外键在子表中的记录。
SET NULL当在父表中删除对应记录时,首先检查该记录是否有对应外键,如果有则设置子表中该外键值为null(这就要求该外键允许取null)
SET DEFAULT父表有变更时,子表将外键设置成一个默认的值(Innodb不支持)

具体语法为:

ALTER TABLE 表名 ADD CONSTRAINT 外键名称 FOREIGN KEY (外键字段名) REFERENCES 主表(主表字段名) ON UPDATE CASCADE ON DELETE CASCADE;

演示如下:

alter table emp add constraint fk_emp_dept_id foreign key (dept_id) references dept(id) on update cascade on delete cascade ;     #CASCADE

alter table emp add constraint fk_emp_dept_id foreign key (dept_id) references dept(id) on update set null on delete set null ;    # SET NULL

五、多表查询

我们之前在讲解SQL语句的时候,讲解了DQL语句,也就是数据查询语句,但是之前讲解的查询都是单表查询,而本章节我们要学习的则是多表查询操作,主要从以下几个方面进行讲解。

5.1 多表关系

在这里插入图片描述

多表查询:多表关系(一对一 多用于单表拆分;多对一;多对多 第三张表)、

5.2 多表查询概述

5.3 内连接

5.4 外连接

5.5 自连接

5.6 子查询

多表查询分类连接查询内连接 相当于查询A、B交集部分数据;外连接 左外连接、右外连接;自查询 当前表与自身的连接查询 自连接必须使用表别名)、子查询

#内连接查询语法 查询两张表交集的部分
# 隐式内连接
SELECT 字段列表 FROM 表1,表2 WHERE 条件...;
# 显式内连接  INNER关键字可以省略
SELECT 字段列表 FROM 表1 [INNER] JOIN 表2 ON 连接条件...;

#如果已为表起了别名,后面不能再通过表名限定字段
select emp.name,dept.name from emp,dept where emp.dept_id = dept.id;
select e.name,d.name from emp e,dept d where e.dept_id = d.id;
select e.name,d.name from emp e join dept d on e.dept_id=d.id;


#外连接查询语法
#左外连接  用得比较多
SELECT 字段列表 FROM 表1 LEFT [OUTER] JOIN 表2 ON 条件...;
#右外连接
SELECT 字段列表 FROM 表1 RIGHT [OUTER] JOIN 表2 ON 条件...;


#自连接查询语法(表必须起别名)   自连接查询,可以是内连接查询,也可以是外连接查询
SELECT 字段列表 FROM 表A 别名A JOIN 表A 别名B ON 条件...;

#查询员工及其所属领导的名字(内连接)
select name, from emp a join emp b on b.manager_id=a.id;  ×
select a.name,b.name from emp a,emp b where a.managerid=b.id;   #a当作员工表,b当作领导表
#查询所有员工emp及其领导的名字emp,如果员工没有领导 也需要查询出来
select a.name '员工',b.name '领导' from emp a left join emp b on a.manager_id = b.id;

联合查询-union、union all。对于union查询 就是把多次查询的结果合并起来,形成一个新的查询结果集。

SELECT 字段列表 FROM 表A ...
UNION[ ALL ]
SELECT 字段列表 FROM 表B...;  #union all直接将结果合并,union将查询的结果去重

# 注:1.对于联合查询的多张表的列数必须保持一致,字段类型也需要保持一致;   2.union all会将全部的数据直接合并在一起,union会对合并之后的数据去重。

**子查询:**SQL语句中嵌套SELECT语句,成为嵌套查询,又称子查询。子查询外部的语句可以是INSERT/UPDATE/DELETE/SELECT的任何一个。

SELECT * FROM t1 WHERE column1=(SELECT column1 FROM t2);

根据子查询结果不同,分为:标量子查询(子查询结果为单个值)、列子查询(子查询结果为一列)、行子查询(子查询结果为一行)、表子查询(子查询结果为多行多列)

根据子查询位置,分为:WHERE之后、FROM之后、SELECT之后。

#标量子查询:子查询返回的结果是单个值(数字、字符串、日期等),最简单的方式,常用的操作符 = <> > >= < <=
select * from emp where dept_id = (select id from dept where name='销售部');   #查询“销售部”的所有员工信息


#列子查询:子查询返回的结果是一列(可以是多行)。常用的操作符  IN、NOT IN、ANY、SOME、ALL
IN        在指定的集合范围内,多选一
NOT IN    不在指定的集合范围之内
ANY       子查询返回列表中,有任意一个满足即可
SOMEANY等同,使用SOME的地方都可以使用ANY
ALL       子查询返回列表的所有值都必须满足

select * from emp where dept_id in (select id from dept where name = '销售部' or name = '市场部');  #查询“销售部”和“市场部”的所有员工信息
select * from emp where salary > all (select salary from emp where dept.id=(select id from dept where name='财务部'));   #查询比财务部所有人工资都高的员工信息      工资 > 财务部所有人员工资
select * from emp where salary > any/some (...);  #比研发部其中任意一人工资高的员工信息


#行子查询:子查询返回的结果是一行(可以是多列)。常用操作符 =、<>、IN、NOT IN
select * from emp where (salary,managerid) = (select salary,managerid from emp where name='张无忌'); #查询与“张无忌”的薪资与直属领导相同的员工信息


#表子查询:子查询返回的结果是多行多列,常用在FROM之后。常用操作符 IN
#查询与“张三”、“李四”的职位和薪资相同的员工信息
select * from emp where (job,salary) in (select job,salary from emp where name='张三' or name='李四');
#查询入职日期是“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;

六、事务

6.1 事务简介

事务:一组操作的集合,是一个不可分割的工作单位,事务会把所有的操作作为一个整体一起向系统提交或撤销操作请求,即这些操作要么同时成功 要么同时失败。

在这里插入图片描述

注意:默认MySQL的事务是自动提交的,也就是说 当执行一条DML语句 MySQL会立即隐式地提交事务。(一条sql语句默认就是一个事务)

6.2 事务操作

6.2.1 未控制事务

在这里插入图片描述

6.2.2 控制事务一

方式一 关闭事务自动提交,通过commit;

# 1.查看/设置事务提交方式
SELECT @@autocommit;
SET @@autocommit=0;    #设为手动提交事务(1为自动提交,0为手动提交 执行完sql之后 运行commit;)
# 2.提交事务   执行完sql之后 运行commit;
COMMIT;
# 3.回滚事务
ROLLBACK;

注意:上述的这种方式,我们是修改了事务的自动提交行为, 把默认的自动提交修改为了手动提交, 此时我们执行的DML语句都不会提交, 需要手动的执行commit进行提交。

6.2.3 控制事务二

方式二 保持autocommit=1,不必设置autocommit=0。

# 1.开启事务
START TRANSACTIONBEGIN;
......     #执行语句
# 2.提交事务。如果所有语句都成功执行,则提交事务 
COMMIT;
# 3.回滚事务。如果在执行过程中发生错误,或者你想回滚事务,则使用ROLLBACK
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 事务四大特性ACID

  • 原子性(Atomicity):事务是不可分割的最小操作单元,要么全部成功,要么全部失败。
  • 一致性(Consistency):事务完成时,必须使所有的数据都保持一致状态。
  • 隔离性(Isolation):数据库系统提供的隔离机制,保证事务在不受外部并发操作影响的独立环境下运行。 在事务开始和完成时,中间过程对其它事务是不可见的。
  • 持久性(Durability):事务一旦提交或回滚,它对数据库中的数据的改变就是永久的。

上述就是事务的四大特性,简称ACID。

在这里插入图片描述

6.4 并发事务问题

并发事务问题:脏写、脏读、不可重复读、幻读

问题描述
脏写(dirty write)两个事务同时更新一行数据,事务A回滚把事务B的值覆盖了,实质就是两个未提交的事务互相影响
脏读(dirty ready)一个事务读到另外一个事务还没有提交的数据。
不可重复读(non-repeatable read)一个事务先后读取同一条记录,但两次读取的数据不同,称之为不可重复读。(其他事务已提交)【针对同一行记录】
幻读(phantom read)一个事务按照条件查询数据时,没有对应的数据行,但是在插入数据时,又发现这行数据已经存在,好像出现了“幻影”【针对数据行数】

在这里插入图片描述

6.4.1 脏写

脏写(dirty write),两个事务同时更新一行数据,事务A回滚把事务B的值覆盖了,实质就是两个未提交的事务互相影响。

CREATE TABLE `bank_balance` (   
    `id` int NOT NULL AUTO_INCREMENT,   
    `user_name` varchar(45) NOT NULL COMMENT '用户名', 
    `balance` int NOT NULL DEFAULT '0' COMMENT '余额,单位:人民币分,比如100表示人民币1元,默认是0',   
    `wealth` tinyint NOT NULL DEFAULT '0' COMMENT '富有程度,0:贫穷,1:富有',   
    PRIMARY KEY (`id`),   
    UNIQUE KEY `idx_bank_balance_user_name` (`user_name`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

表中有两行数据,我们操作id=2、user_name='Tom’的记录

在这里插入图片描述

现在有两个事务,事务A和事务B,事务A是给Tom账户余额加100,事务B是给Tom账户余额加200。

#对于事务A,执行如下
start transaction;
update bank_balance set balance=balance+100 where user_name='Tom';
rollback;    #失败回滚

#对于事务B,执行如下
start transaction;
update bank_balance set balance=balance+200 where user_name='Tom';
COMMIT;

在这里插入图片描述

在①处,事务A得到的余额是200,事务B得到的余额是300,如果事务B是后更新,那么就覆盖了事务A的值。

在②处,事务 A 和事务B都没有提交的情况下,它们随时都有可能发生回滚,如上图这种情况事务 A 发生了回滚,然后事务B再提交,那么对于事务 B 看到的场景而言,就是自己明明更新了,结果值却还是旧值,这就是脏写

6.4.2 脏读

脏读(dirty read),指的是读到了其他事务未提交的数据,未提交意味着可能会回滚,也就是可能最终不会持久化到数据库中。其他事务读到了不会持久化的数据,这就是脏读。

在这里插入图片描述

比如下图,如果事务A在①处发生回滚,那么事务B在②处使用的Tom余额值200就是一个过期值,这种就是典型的脏读现象

小结

  • 无论是脏写还是脏读,都是因为一个事务去更新或者查询了另外一个还没提交的事务更新后的数据
  • 因为另外一个事务还没提交,所以它随时可以会反悔,那么必然导致你更新的数据没了,或者之前查询的数据没了。这就是脏写和脏读两种典型场景。

6.4.3 不可重复读

不可重复读(non-repeatable read),指的是在同一事务内,相同数据在不同的时刻被读到了不一样的值,它和脏读不一样,脏读是指读取到了其他事务未提交的数据,而不可重复读表示读到了其他事务修改并提交后的值。

比如有两个事务,事务A和事务B,事务A查询Tom账户余额是100,事务B查询Tom账户余额也是100。

在这里插入图片描述

接下来,事务A把Tom账户余额更新为200,并提交事务。

当事务B继续读取Tom账户余额的时候,发现Tom账户余额是200了,和之前读取到的不一致,对于事务B而言,这种一个事务内多次读取得到不一样值的现象就称为不可重复读现象

6.4.4 幻读

幻读(phantom read),主要是是针对数据插入(INSERT)和删除(DELETE)操作来说的。具体是指,一个事务在前后两次查询同一个范围的时候,后一次查询看到了前一次查询没有看到的行

最经典的是插入的情况。假如现在有两个事务,事务A和事务B。事务A对某些行的内容作了更改,但是还未提交。

在这里插入图片描述

比如现在余额表中余额大于0的账户有2条,分别是Jenny和Tom,他们的富有程度都是贫穷。然后,接到上级命令,要把所有账户余额大于0的用户全部标识为富有,启动事务A完成这项任务,SQL如下:

start transaction;
update bank_balance set wealth = 1 where balance > 0;

SQL语句只是执行了,但是未提交。

紧接着,事务B插入了一条余额大于0的记录行(富有程度默认为贫穷),并且在事务A提交之前先提交了,SQL如下:

INSERT INTO `bank_balance` (`id`, `user_name`, `balance`) VALUES ('3', 'Jack', '500');

在这之后,如果事务A再发起相同条件的查询,会发现刚刚的更改对于某些数据未起作用(有些记录未被标识为富有),而且数据行比原来还多了!

在这里插入图片描述

这对于事务A而言,感觉出现了幻觉一样,这就是幻读现象

产生幻读的原因是,行锁只能锁住行,但是新插入记录这个动作,操作的是锁住的行之间的 “间隙”。因此,为了解决幻读问题,InnoDB 只好引入新的锁,也就是间隙锁 (Gap Lock)。

6.4.5 区别、小结

  • 脏写(dirty write):两个事务未提交的情况下,同时更新一行数据。事务A回滚,把事务B修改的值覆盖了,实质就是两个未提交的事务修改同一个值、互相影响。
  • 脏读(dirty read):指的是读到了其他事务未提交的数据。事务A修改一条数据的值,还未提交,事务B就读到了A修改的值;结果A回滚了,事务B之前读的就是一个过期值,即事务读到了修改之后没有提交的值
  • 不可重复读(non-repeatable read):指的是在一个事务内多次读取同一条数据 得到不一样的值。这个过程中可能其他事务会修改数据,并且修改之后事务都提交了。它和脏读不一样,脏读是指读取到了其他事务未提交的数据,而不可重复读表示读到了其他事务修改并提交后的值。
  • 幻读(phantom read):一个事务在前后两次查询同一个范围的时候,后一次查询看到了前一次查询没有看到的行。被其他事务插入或者删除的数据影响,一个事务内同样条件的数据记录变多或者变少了。

上面四个问题都是因为业务系统会多线程并发执行,每个线程可能都会开启一个事务,每个事务都会执行增删改查操作。

然后数据库会并发执行多个事务,多个事务可能会并发地对缓存页里的同一批数据进行增删改查操作,可能就会导致脏写、脏读、不可重复读、幻读这些问题。

因此这些问题的本质,就是数据库的多事务并发问题。为了解决多事务并发问题,数据库才设计了事务隔离机制、MVCC多版本隔离机制、锁机制,用一整套机制来解决多事务并发问题。

6.5 事务隔离级别

为了解决并发事务所引发的问题,在数据库中引入了事务隔离级别,且不同级别的隔离可以规避不同严重程度的事务问题。主要有以下几种:

  1. 读未提交(READ UNCOMMITTED),指一个事务还没提交,它做的修改就能被其他事务看到。
  2. 读提交(READ COMMITTED),一个事务做的修改,只有提交之后,其他事务才能看到。
  3. 可重复读(REPEATABLE READ),在整个事务过程中看到的数据,自始至终都是一致的。
  4. 串行化(SERIALIZABLE),每个读写操作都会加锁,多个事务要访问同一条记录时,必须要进行排队,优先级低的事务必须等优先级高的事务完成以后才能进行。

从1到4,隔离级别依次变高,当然,性能也依次变差。那么这些隔离级别究竟都能防止哪些问题呢

隔离级别脏读不可重复读幻读
Read uncommitted√会出现
Read committed×不会出现
Repeatable Read(MySQL默认)××
Serializable 隔离级别最高、性能最差×××

注:事务隔离级别 事务隔离级别越高,数据越安全,但是性能越低。一般采用数据库的默认级别。

MySQL InnoDB引擎默认的隔离级别是可重复读(RR)。

# 查看事务隔离级别
SELECT @@TRANSACTION_ISOLATION;
SELECT @@tx_isolation;

# 设置事务隔离级别
SET [SESSION|GLOBAL] TRANSACTION ISOLATION LEVEL {READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE};

1.MySQL InnoDB引擎默认的隔离级别是可重复读(RR)。为什么MySQL没有使用串行化这个级别?是不是意味着我们日常使用MySQL会有可能存在幻读的问题?

非也!隔离级别越高代价也是越高的,且性能也越差。从性能上来说,当然是隔离级别越低越好。

2.至于隔离级别是RR(可重复读)下的MySQL怎么避免幻读问题,InnoDB引擎有它自己的想法,以后单独抽一讲来说啦

我们再来看一张图,理解不同隔离级别下读取到的数据是怎么样的:

在这里插入图片描述

有两个事务,事务A和事务B,同时操作(查询或者给Tom余额加100),事务B在事务A提交前更新了Tom的余额,并且事务B在事务A前提交。

  1. 读未提交隔离级别下,事务 B 修改余额后,事务 A 能够马上看见,即使事务B还未提交,所以事务 A 中余额 R1 查询的值是 200,余额 R2、R3 也是 200.

  2. 读提交隔离级别下,事务 B 修改余额后,只有事务B提交后事务A才能看见,所以事务A中余额R1查询在提交前,查的值是100,余额R2和余额R3都是在事务B提交后,查询得到的值都是200。

  3. 可重复读隔离级别下,事务A在提交前自始至终查到的值都必须一样,所以,余额R1、R2都是100,当事务A提交后再查询(其实是新事务)就能查到新的值,所以R3是200。

  4. 串行化隔离级别下,MySQL会给记录行以及记录行之间的’空行’加锁,如果是A事务先获得锁,那么B事务必须等到A事务提交以后才能更新数据。

    比如上图,如果事务A查询Tom余额的SQL条件是’where user_name = “Tom”',user_name有唯一索引,所以只会给Tom账户这一行数据加共享锁。

    当B事务要去更新Tom的账户余额时,是获取不到锁的,必须等待直至事务A完全提交。

    所以以上R1、R2查询得到的值都是100(这个时候事务B在排队等待),事务A提交以后, 事务B就可以更新值并提交了,R3是在事务B提交之后查询,所以是200。

参考黑马程序员mysql课程、还傻傻搞不懂MySQL事务隔离级别么(图文并茂,保证你懂!)

  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值