MySQL 数据库 入门到精通 纯手敲笔记(基础篇)
黑马程序员 MySQL数据库入门到精通,从mysql安装到mysql高级、mysql优化全囊括
https://www.bilibili.com/video/BV1Kr4y1i7ru
- 要学的内容:SQL、事务、存储引擎、索引、SQL优化、锁、日志、主从复制、读写分离、分库分表

连接数据库(默认):mysql -u root -p
若多个数据库管理工具,指定版本:mysql -h localhost -P 3306 -u root -p
基础篇
- MYSQL概述
- SQL
- 函数
- 约束
- 多表查询
- 事务
SQL
- DDL 数据定义语言,定义数据库对象
- DML 数据操作语言,增删改
- DQL 数据查询语言,查
- DCL 数据控制语言,创建数据库用户,控制数据库的访问权限
字段数据类型
- 数值类型

TINYINT SMALLINT MEDIUMINT INT BIGINT FLOAT DOUBLE DECIMAL
无符号 eg: 年龄 age TINYINT UNSIGNED
DOUBLE(整体长度,小数位数) eg: score DOUBLE(4,1);
- 字符串类型

char(10)未占用空间用空格补充,性能高 eg:gender
varchar(10)可变长,占用空间不固定,性能较差 eg:username
- 日期类型

DDL数据定义语言
1. 数据库操作
- 查询
查询所有数据库:SHOW DATABESES
查询当前数据库:SELECT DATABASE() (与USE使用数据库对应)
- 创建
CREATE DATABASE [IF NOT EXISTS] 数据库名 [DEFAULT CHARSET 字符集] [COLLATE 排序规则]
(字符集建议使用utf8mb4)
(DATABASE也可以是SCHEMA)
- 删除
DROP DATABASE [IF EXISTS] 数据库名
- 使用
USE 数据库名
2. 表操作
- 查询库中所有表
SHOW TABLES;(需要先use)
- 查询表结构
DESC 表名;
- 查询建表语句
SHOW CREATE TABLE 表名;
- 创建
CREATE TABLE 表名(
字段1 类型[COMMENT 注释],
字段2 类型[COMMENT 注释],
)[COMMENT 注释];
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 '员工表';
修改
字段:
- 添加字段
ALTER TABLE 表名 ADD 字段名 类型(长度) [COMMENT 注释];
- 修改数据类型
ALTER TABLE 表名 MODIFY 字段名 新数据类型(长度);
- 修改字段名和字段类型
ALTER TABLE 表名 CHANGE 字段名 新字段名 类型(长度) [COMMENT 注释] [约束];
表:
- 修改表名
ALTER TABLE 表名 RENAME TO 新表名;
删除
字段:
- 删除某字段
ALTER TABLE 表名 DROP 字段名;
表:
- 删除表(删除表时,所有数据也会被删除)
DROP TABLE [IF EXISTS] 表名;
- 删除并重新创建该表
TRUNCATE TABLE 表名;
DML增删改
- 添加数据
(1) 给指定字段添加数据
INSERT INTO 表名 (字段1,字段2) VALUES (值1,值2);
(2) 给全部字段添加数据
INSERT INTO 表名 VALUES (值1,值2)
(3) 批量添加数据
INSERT INTO 表名 (字段1,字段2) VALUES (值1,值2),(值1,值2),(值1,值2)
INSERT INTO 表名 VALUES (值1,值2),(值1,值2),(值1,值2)
(注意:字符串类型和日期类型需要引号包裹)
- 修改数据
UPDATE 表名 SET 字段名1=值1, 字段名2=值2 , ... [WHERE 条件];
- 删除数据
DELETE FROM 表名 [WHERE 条件];
DQL查询
基本查询、条件查询、聚合函数、分组查询、排序查询、分页查询
基本查询
- 查询多个字段
SELECT 字段1,字段2,字段3... FROM 表名;
SELECT * FROM 表名;
- 设置别名
SELECT 字段1[AS 别名1],字段2[AS 别名2]... FROM 表名;
- 去除重复记录(DISTINCT)
SELECT DISTINCT 字段列表 FROM 表名;
条件查询
SELECT 字段列表 FROM 表名 WHERE 条件列表;

# 查询年龄88岁的员工
select * from emp where age=88;
# 查询年龄小于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]的员工(between左界and右界)
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;
# 查询年龄等于10或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';
聚合函数
将一列数据作为整体,进行纵向计算
常见的聚合函数:count 统计数量,max 最大值,min 最小值,avg 平均值,sum 求和
SELECT 聚合函数(字段列表) FROM 表名;
# 统计员工数量
select count(*) from emp;
select count(idcard) from emp; #null不参与聚合函数运算
# 统计员工平均年龄
select avg(age) from emp;
# 统计最大年龄
select max(age) from emp;
# 统计最小年龄
select min(age) from emp;
# 统计西安地区员工年龄之和
select sum(age) from emp where workaddress='西安';
分组查询
SELECT 字段列表 FROM 表名 [WHERE 条件] GROUP BY 分组字段名 [HAVING 分组后过滤条件];
where与having区别
执行时机不同:where是分组之前进行过滤,不满足where条件,不参与分组;而having是分组之后对结果进行过滤。
判断条件不同:where不能对聚合函数进行判断,而having可以。
# 根据性别分组,统计男性员工和女性员工的数量
select count(*) from emp group by gender;
select gender,count(*) from emp group by gender;
# 根据性别分组,统计男性员工和女性员工的平均年龄
select gender,avg(age) from emp group by gender;
# 查询年龄小于 45 的员工,并根据工作地址分组,获取员工数量大于等于 5 的工作地址
select workaddress,count(*) address_count from emp where age<45 group by workaddress having address_count >=3; # 这里使用别名,省略了as关键字
排序查询
SELECT 字段列表 FROM 表名 ORDER BY 字段1 排序方式1, 字段2 排序方式2;
排序方式:ASC:升序(默认值),DESC:降序(是 DESCENDING 的缩写,而展示表结构的DESC 是 DESCRIBE 的简写)
-- 排序查询
-- 1. 根据年龄对公司的员工进行升序排序
select * from emp order by age asc;
select * from emp order by age;
-- 2. 根据入职时间,对员工进行降序排序
select * from emp order by entrydate desc;
-- 3. 根据年龄对公司的员工进行升序排序,年龄相同,再按照入职时间进行降序排序
select * from emp order by age asc, entrydate desc;
分页查询
SELECT 字段列表 FROM 表名 LIMIT 起始索引,查询记录数;
起始索引从 0 开始,起始索引 =(查询页码 - 1)* 每页显示记录数。
分页查询是数据库的方言,不同的数据库有不同的实现,MySQL 中是 LIMIT。
如果查询的是第一页数据,起始索引可以省略,直接简写为 limit 10。
-- 分页查询
-- 1. 查询第1页员工数据,每页展示10条记录
select * from emp limit 0,10;
select * from emp limit 10;
-- 2. 查询第2页员工数据,每页展示10条记录
select * from emp limit 10,10;
练习
按照需求完成如下 DQL 语句编写
- 查询年龄为 20,21,22,23 岁的员工信息。
- 查询性别为男,并且年龄在 20 - 40 岁 (含) 以内的姓名为三个字的员工。
- 统计员工表中,年龄小于 60 岁的,男性员工和女性员工的人数。
- 查询所有年龄小于等于 35 岁员工的姓名和年龄,并对查询结果按年龄升序排序,如果年龄相同按入职时间降序排序。
- 查询性别为男,且年龄在 20 - 40 岁 (含) 以内的前 5 个员工信息,对查询的结果按年龄升序排序,年龄相同按入职时间升序排序。
select * from emp where age between 20 and 23;
select * from emp where gender='男' and age between 20 and 40 and name like '___';
select gender,count(*) as gender_count from emp where age<60 group by gender;
select name,age from emp where age<=35 order by age asc, entrydate desc;
select * from emp where gender='男' and age between 20 and 40 order by age, entrydate limit 5;
执行顺序

DCL控制
管理用户
用户等信息都是存放在系统数据库中的
- 查询用户
USE mysql;
SELECT * FROM user;
- 创建用户
CREATE USER '用户名'@'主机名' IDENTIFIED BY '密码';
- 修改用户密码
ALTER USER '用户名'@'主机名' IDENTIFIED WITH mysql_native_password BY '新密码'';
- 删除用户
DROP USER '用户名'@'主机名';
-- 创建用户 itcast ,只能够在当前主机localhost访问,密码23456;
CREATE USER 'itcast'@'localhost' IDENTIFIED BY '23456';
-- 创建用户 heima ,可以在任意主机访问该数据库,密码123456 ;
CREATE USER 'heima'@'%' IDENTIFIED BY '123456';
-- 修改用户 heima 的访问密码为 1234 ;
ALTER USER 'heima'@'%' IDENTIFIED BY '1234';
-- 删除itcast@localhost用户
DROP USER 'itcast'@'localhost';
(主机名可以使用%通配)
权限控制

- 查询权限
SHOW GRANTS FOR '用户名'@'主机名';
- 授予权限
GRANT 权限列表 ON 数据库名.表名 TO '用户名'@'主机名';
- 撤销权限
REVOKE 权限列表 ON 数据库名.表名 FROM '用户名'@'主机名';
-- 查询权限
show grants for 'heima'@'%';
-- 授予权限
grant all on itcast.* to 'heima'@'%';
-- 撤销权限
revoke all on itcast.* from 'heima'@'%';
多个权限之间,使用逗号分隔。
授权时,数据库名和表名可以使用。进行通配,代表所有。
函数
- 字符串函数
- 数值函数
- 日期函数
- 流程函数
字符串函数

(substring的索引从1开始)
# 工号升级为5位数(左填充0)
update emp set workno = lpad(workno, 5, '0');
数值函数

# 生成6位数随机验证码
select lpad(round(rand()*1000000,0),6,'0');
日期函数

# 70天后的时间
select date_add(now(), INTERVAL 70 DAY);
# 判断时间差
select datediff('2021-12-01','2021-11-01');
# 查询所有员工的入职天数,并根据入职天数倒序排序
select name,datediff(now(),entrydate) as entrydays from emp order by entrydays desc;
流程函数

select if(true,'ok','error');
select ifnull(null,'default');
# 查询员工表的姓名和工作地址(北京/上海->一线城市,其他-> 二线城市)
select
name,
(case workaddress when'北京' then '一线城市' when '上海' then '一线城市' else '二线城市' end) as 工作地址
from emp;
# 统计学生成绩等级
select
id,
name,
(case when math>=85 then '优秀' when math>=60 then '及格' else '不及格' end) as '数学',
(case when english>=85 then '优秀' when english>=60 then '及格' else '不及格' end) as '英语',
(case when chinese>=85 then '优秀' when chinese>=60 then '及格' else '不及格' end) as '语文'
from score;
约束

AUTO_INCREMENT
-- id:ID唯一标识,字段类型int,约束为主键且自动增长(PRIMARY KEY, AUTO_INCREMENT)
-- name:姓名,字段类型varchar(10),约束为不为空且唯一(NOT NULL, UNIQUE)
-- age:年龄,字段类型int,约束为大于0且小于等于120(CHECK)
-- status:状态,字段类型char(1),约束为未指定值时默认1(DEFAULT)
-- gender:性别,字段类型char(1),无特殊约束
create table user (
id int primary key auto_increment comment '主键',
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 '姓名'
) comment '用户表';
外键约束
CREATE TABLE 表名 (
字段名 数据类型,
...
[CONSTRAINT] [外键名称] FOREIGN KEY (外键字段名) REFERENCES 主表(主表列名)
)
ALTER TABLE 表名 ADD CONSTRAINT 外键名称 FOREIGN KEY (外键字段名) REFERENCECS 主表(主表列名);
alter table emp add constraint fk_emp_dept_id foreign key (dept_id) references dept(id);
(在datagrip中关掉表再打开表在新标签页即可看到外键蓝色钥匙标识)
alter table emp drop foreign key fk_emp_dept_id;
外键删除/更新行为

ALTER TABLE 表名 ADD CONSTRAINT 外键名称 FOREIGN KEY (外键字段) REFERENCES 主表名(主表字段名) ON UPDATE CASCADE ON DELDETE CASCADE;
多表查询
- 多表关系
- 多表查询概述
- 内连接
- 外连接
- 自连接
- 子查询
- 多表查询案例
多表关系
- 一对多(多对一)
在多的一方建立外键,指向一的一方的主键
- 多对多
建立第三张中间表,中间表至少包含两个外键,分别关联两方主键
- 一对一
在任意一方加入外键,关联另外一方的主键,并且设置外键为唯一的(UNIQUE)
多表查询概述
笛卡尔积
select * from emp, dept;
消除笛卡尔积
select * from emp, dept where emp.dept_id = dept.id;
多表查询分类
连接查询
- 内连接:相当于查询A、B交集部分数据
隐式内连接
SELECT 字段列表 FROM 表1,表2 WHERE 条件...;
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 字段列表 FROM 表1 [INNER] JOIN 表2 ON 连接条件...;
select e.name,d.name from emp e inner join dept d on e.dept_id = d.id;
# inner 关键字可省略
-
外连接:
左外连接:查询左表所有数据,以及两张表交集部分数据
SELECT 字段列表 FROM 表1 LEFT [OUTER] JOIN 表2 ON 条件...;
select e.*,d.name from emp e left outer join dept d on e.dept_id = d.id; # outer 关键字可省略右外连接:查询右表所有数据,以及两张表交集部分数据
SELECT 字段列表 FROM 表1 RIGHT [OUTER] JOIN 表2 ON 条件...;
select e.*, d.* from emp e right outer join dept d on e.dept_id = d.id; -
自连接:当前表与自身的连接查询,自连接必须使用表别名
SELECT 字段列表 FROM 表A 别名A JOIN 表A 别名B ON 条件...;
eg: 在一张企业员工信息表中,有员工id、姓名、所属领导的员工id,要查询各员工的领导姓名,就需要用到自连接(相当于两张表,员工表和领导表,员工表的managerid关联领导表的id)
select a.name 员工, b.name 领导 from emp a left join emp b on a.managerid = b.id;
联合查询
对于union查询,就是把多次查询的结果合并起来,形成一个新的查询结果集。
SELECT 字段列表 FROM 表A...
UNION [ALL]
SELECT 字段列表 FROM 表B...;
select * from emp where salary<5000
union all
select * from emp where age>50;
# 以上仅把两次查询结果连接,
# 如需去重,删去all即可
select * from emp where salary<5000
union
select * from emp where age>50;
对于联合查询的多张表的列数必须保持一致,字段类型也需要保持一致。
union all 会将全部的数据直接合并在一起,union 会对合并之后的数据去重。
子查询
SQL语句中嵌套SELECT语句,称为嵌套查询,又称子查询。
SELECT * FROM t1 WHERE column1 = (SELECT colomn1 FROM t2);
子查询外部的语句可以是INSERT/UPDATE/DELETE/SELECT的任何一个。
根据子查询的结果不同,可分为:
标量子查询(子查询结果为单个值),
列子查询(子查询结果为一列),
行子查询(子查询结果为一行),
表子查询(子查询结果为多行多列)。
根据子查询的位置,分为:WHERE之后,FROM之后,SELECT之后。
- 标量子查询
-- 查询销售部的所有员工信息
-- a. 查询销售部的部门id
select id from dept where name = '销售部';
-- b. 根据部门id,查询员工信息
select * from emp where dept_id = 4;
-- 合并成嵌套查询
select * from emp where dept_id = (select id from dept where name = '销售部');
-- 查询在方东白入职之后的员工信息
-- a. 查询方东白的入职日期
select entrydate from emp where name = '方东白';
-- b. 查询指定入职日期之后的员工信息
select * from emp where entrydate>'2009-02-12';
-- 合并成嵌套查询
select * from emp where entrydate>(select entrydate from emp where name = '方东白');
- 列子查询
子查询结果为一列。
常用操作符:IN、NOT IN、ANY、SOME、ALL

-- 查询销售部和市场部的所有员工信息
-- a.查询销售部和市场部的部门id
-- b.根据id查询员工信息
select * from emp where 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 > (
SELECT MAX(salary)
FROM emp
WHERE dept_id = (SELECT id FROM dept WHERE name = '财务部')
);
-- 查询比任意一个研发部员工工资高的员工信息
select * from emp where salary > any (select salary from emp where dept_id = (select id from dept where name = '研发部'))
- 行子查询
子查询返回结果是一行。
常用的操作符:=、<>、IN、NOT IN
-- 查询与张无忌的薪资及直属领导相同的员工信息
-- a.查询张无忌的薪资及直属领导
select salary,managerid from emp where name='张无忌';
-- b.查询与张无忌的薪资及直属领导相同的员工信息
select * from emp where salary = 12500 and managerid = 1;
-- 上面b语句可以写成
select * from emp where (salary,managerid) = (12500,1);
-- 合并成嵌套语句
select * from emp where (salary,managerid) = (select salary,managerid from emp where name = '张无忌');
- 表子查询
子查询返回的结果是多行多列。
常用的操作符:IN。
-- 查询与鹿杖客、宋远桥的职位和薪资相同的员工信息
-- a.查询鹿杖客、宋远桥的职位和薪资
select job,salary from emp where name = '鹿杖客' or name = '宋远桥';
-- b.合并子查询,根据鹿杖客、宋远桥的职位和薪资查询员工信息
select * from emp where (job,salary) in (select job,salary from emp where name = '鹿杖客' or name = '宋远桥');
-- 查询入职日期是2006-01-01之后的员工信息,及其部门信息★
-- a.查询入职日期是2006-01-01之后的员工
select * from emp where entrydate > '2006-01-01';
-- b.查询这部分员工,对应的部门信息
select e.*,d.* from (select * from emp where entrydate > '2006-01-01') as e left join dept d on e.dept_id = d.id;
-- 以上也可以直接写成:
SELECT e.*, d.*
FROM emp e
LEFT JOIN dept d ON e.dept_id = d.id
WHERE e.entrydate > '2006-01-01';
案例
根据需求,完成 SQL 语句的编写
- 查询员工的姓名、年龄、职位、部门信息。
- 查询年龄小于 30 岁的员工姓名、年龄、职位、部门信息。
- 查询拥有员工的部门 ID、部门名称。
- 查询所有年龄大于 40 岁的员工,及其归属的部门名称;如果员工没有分配部门,也需要展示出来。
- 查询所有员工的工资等级。
- 查询 “研发部” 所有员工的信息及工资等级。
- 查询 “研发部” 员工的平均工资。
- 查询工资比 “灭绝” 高的员工信息。
- 查询比平均薪资高的员工信息。
- 查询低于本部门平均工资的员工信息。
- 查询所有的部门信息,并统计部门的员工人数。
- 查询所有学生的选课情况,展示出学生名称,学号,课程名称
-- 1. 查询员工的姓名、年龄、职位、部门信息(隐式内连接)
-- 表: emp , dept
-- 连接条件: emp.dept_id = dept.id
select e.name , e.age , e.job , d.name from emp e , dept d where e.dept_id = d.id;
-- 2. 查询年龄小于30岁的员工的姓名、年龄、职位、部门信息(显式内连接)
-- 表: emp , dept
-- 连接条件: emp.dept_id = dept.id
select e.name , e.age , e.job , d.name from emp e inner join dept d on e.dept_id = d.id where e.age < 30;
-- 3. 查询拥有员工的部门ID、部门名称
-- 表: emp , dept
-- 连接条件: emp.dept_id = dept.id
-- (内联能去除部门字段为null的员工)
select distinct d.id , d.name from emp e , dept d where e.dept_id = d.id;
-- 或者
select distinct d.id, d.name from dept d inner join emp e on d.id=e.dept_id;
-- 4. 查询所有年龄大于40岁的员工,及其归属的部门名称;如果员工没有分配部门,也需要展示出来
-- 表: emp , dept
-- 连接条件: emp.dept_id = dept.id
-- 外连接
select e.*, d.name from emp e left join dept d on e.dept_id = d.id where e.age > 40 ;
-- 5. 查询所有员工的工资等级
-- 表: emp , salgrade
-- 连接条件: emp.salary >= salgrade.losal and emp.salary <= salgrade.hisal
select e.*, s.grade, s.losal, s.hisal from emp e , salgrade s where e.salary >= s.losal and e.salary <= s.hisal;
select e.*, s.grade, s.losal, s.hisal from emp e , salgrade s where e.salary between s.losal and e.salary <= s.hisal; -- 这里原语句可能有误,正确应为 between s.losal and s.hisal
-- 6. 查询 “研发部” 所有员工的信息及工资等级
-- 表: emp , salgrade , dept
-- 连接条件 : emp.salary between salgrade.losal and salgrade.hisal, emp.dept_id = dept.id
-- 查询条件 : dept.name = '研发部'
select e.*, s.grade
from emp e,
dept d,
salgrade s
where e.dept_id = d.id
and (e.salary between s.losal and s.hisal)
and d.name = '研发部';
-- 7. 查询 “研发部” 员工的平均工资
-- 表: emp , dept
-- 连接条件 : emp.dept_id = dept.id
select avg(e.salary) from emp e, dept d where e.dept_id = d.id and d.name = '研发部';
-- 8. 查询工资比 “灭绝” 高的员工信息。
-- a. 查询 “灭绝” 的薪资
select salary from emp where name = '灭绝';
-- b. 查询比 “灭绝” 工资高的员工数据
select * from emp where salary > ( select salary from emp where name = '灭绝' );
-- 9. 查询比平均薪资高的员工信息
-- a. 查询员工的平均薪资
select avg(salary) from emp;
-- b. 查询比平均薪资高的员工信息
select * from emp where salary > ( select avg(salary) from emp );
-- 10. 查询低于本部门平均工资的员工信息
-- a. 查询指定部门平均薪资
select avg(e1.salary) from emp e1 where e1.dept_id = 1;
select avg(e1.salary) from emp e1 where e1.dept_id = 2;
-- b. 查询低于本部门平均工资的员工信息
select * from emp e2 where e2.salary < ( select avg(e1.salary) from emp e1 where e1.dept_id = e2.dept_id );
-- 11. 查询所有的部门信息,并统计部门的员工人数
select d.id, d.name , ( select count(*) from emp e where e.dept_id = d.id ) '人数' from dept d;
select count(*) from emp where dept_id = 1;
-- 12. 查询所有学生的选课情况,展示出学生名称,学号,课程名称
-- 表: student , course , student_course
-- 连接条件: student.id = student_course.studentid , course.id = student_course.courseid
select s.name, s.no, c.name from student s , student_course sc , course c where s.id = sc.studentid and sc.courseid = c.id ;
事务
事务操作
- 查看/设置事务提交方式
SELECT @@autocommit;
SET @@autocommit = 0; (设置为手动提交)(影响整个会话)
- 开启事务
START TRANSACTION 或 BEGIN; (临时性)
- 提交事务
COMMIT;
- 回滚事务
ROLLBACK;
事务四大特性
-
原子性 Automicity
-
一致性 Consistency
-
隔离性 Isolation
-
持久性 Durability
并发事务问题

事务隔离级别

- 查看事务隔离级别
SELECT @@TRANSACTION_ISOLATION
- 设置事务隔离级别
SET [SESSION | GLOBAL] TRANSACTION ISOLATION LEVEL (READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE)
select @@transaction_isolation;
set session transaction isolation level repeatable read;
浙公网安备 33010602011771号