B站视频学习链接:https://www.bilibili.com/video/av56473701/?p=1【黑马程序员-Java语言高级部分10】MySQL入门学习(黑马并没有给我钱QAQ)
视频中的案例和笔记链接:(在b站视频下方也有):链接:https://pan.baidu.com/s/1Sdh8GH049aIPBkSGaamlyA 提取码:h34l
说一些自己的学习心得
我是一个在校学生,也是完全零基础学习数据库的小白(非计算机专业),网上关于数据库的笔记包括视频应该是非常丰富的,技术上比我强的人肯定是比比皆是,所以我就分享一下自己的一些心得。
1.选择看书学习还是看视频学习
于我而言,正好现在是放假,之前在zhihu上找一些适合零基础自学看的书,在网上找到了pdf后用书学了一两天,发现放假在家根本学不进去,就果断转战看视频学习,中国大学mooc,慕课网,优酷,b站等等都有相关课程,我选择的标准首先是视频教学和自己电脑用的是一个系统,windows和mac在操作上还是会有一定的区别,其次也是我认为最重要的就是一定要能听进去他的视频,讲课最好抑扬顿挫,不要听着很枯燥,我看了b站的视频后没有选择播放量最高的那个,而是选择了上面黑马的视频作为我的入门视频。
2.一定要做笔记,动手敲代码
一定要做笔记,做笔记不是要原封不动的照抄代码,照抄注释,是理清自己看视频学习的思路的,做笔记是为了让自己看的,没必要弄的花里胡哨的,可以参考老师的笔记和注释,但尽量简明扼要,也要有自己的理解,老师的代码尽量也要自己动手尝试,因为有些问题不动手敲代码是找不到的,我也把自己遇到的问题写成tips放在笔记中方便自己日后查看,就算是动手抄一遍代码也不要复制粘贴,写的过程也是记忆的过程。
3.坚持
选好了一个视频之后就是要坚持学习,我看的这个视频说长不长,说短不短,放假在家嘛,效率肯定不会很高,可能每天只能看完一小节的视频,我每天自己给自己卡着最低任务量,保证能在规定时间内完成,虽然最后还是没按时看完(捂脸),但是没有拖延太久,自己还是比较满意。
以下是我自己做的笔记,仅供参考哦嘿嘿,有些不想自己打字的还是直接copy老师的笔记的,大部分是自己写的,笔记都是在视频的基础上写的,如有错误希望批评指正嘻嘻。
唔,给自己插播一个广告,我自己写了一个公众号,分享自己作为一名大学生的成长经历包括自己学德语的一些经验分享,20岁的我没有足够优秀也没有那么富裕,出国,考研,就业,我也不知道我会选择哪条道路,如果想看我的故事或者和我一起成长,那就关注它吧:老梁的日常
* MySQL服务启动
1. 手动。
2. cmd--> services.msc 打开服务的窗口
3. 使用管理员打开cmd
* net start mysql : 启动mysql的服务
* net stop mysql:关闭mysql服务
* MySQL登录
1. mysql -uroot -p密码
2. mysql -hip -uroot -p连接目标的密码
3. mysql --host=ip --user=root --password=连接目标的密码
* MySQL退出
1. exit
2. quit
1. 创建数据
create database 名称 ;
create database if not exists 数据库名称;
--判断是否已经存在数据库
create database 数据库名称 character set 字符集名;
2.查询数据库
show databases;
show create database 数据库名称;
–查看某个数据库的定义信息
3.修改数据库(字符集)
alter database 数据库名称 character set 字符集名称;
4.删除数据库
drop database 数据库名称;
drop database if exists 数据库名称;
5.使用数据库
· 查询当前使用数据库名称 :select database();
· 使用数据库use 数据库名称;
1.创建表
create table 表名(列名1 数据类型1 , 列名2 数据类型2)
–逗号前后的空格可有可无
create table 表名 like 被复制的表名;
--· 快速创建一个表结构相同的表
int:整数类型
double:小数类型
score double(5,2)
(一共五位,小数点后两位)
date:日期,只包含年月日,yyyy-MM-dd
datetime:日期,包含年月日时分秒 yyyy-MM-dd HH:mm:ss
timestamp:时间戳类型 包含年月日时分秒 yyyy-MM-dd
HH:mm:ss
varchar:字符串
字符类型varchar中必须要有括号和括号中的数字来规范字符长度
2.查询表
show tables;
–查询某个数据库中的所有表
desc 表名;
–查询某个表的结构:域、数据类型等
3.修改表
alter table 表名 rename to 新的表名;
–重命名
RENAME TABLE 表名 TO 新表名;
–重命名
alter table 表名 character set 字符集名称;
–修改字符集
alter table 表名 add 列名 数据类型;
–添加一列
alter table 表名 change 列名 新列别 新数据类型;
–修改类的名称
alter table 表名 modify 列名 新数据类型;
–修改列的类型
不能只修改列名而不更改数据类型
alter table 表名 drop 列名;
–删除列
4.删除表
drop table 表名;
drop table if exists 表名 ;
添加数据
insert into 表名(列名1,列名2,...列名n) values(值1,值2,...值n);
insert into form (id,age,name)values(1,18,'无缝');
insert into 表名 values(值1,值2,...值n);
–可不写列名就是向所有的列添加数据
删除数据
delete from 表名 [where 条件]
delete from 表名;
--删除所有,一条一条删,效率低
truncate table 表名;
--删除所有,删除整个表,再创建一个新的表,效率高
修改数据
update 表名 set 列名1 = 值1, 列名2 = 值2,... [where 条件];
基础查询
select * from 表名
--查询所有数据
select 字段列表1 列表2 from 表名列表 where 条件列表 group by 分组字段 having 分组之后的条件 order by 排序 limi t 分页限定
– 查询语法
SELECT id,name(字段)from student(表名);
select DISTINCT address(字段) from student
–去除重复结果
要注意空字符和数据格式,否则可能无法得到正确结果,如果字段为多个,需要每个字段结果都一致才可以去重。
select name,math,english,math+english from student;
--计算列
select name,math,english,math+IFNULL(english,0) AS 总分 from student;
select name,math 数学,english 英语,math+IFNULL(english,0) AS 总分 from student;
--处理出现null用ifnull,用as给查询字段起别名
条件查询
select * from student where age = 20;
--mysql中没有==,只用一个=号。
select * from student where age <>或!= 20;
--<>是不等于的符号,和别的语言相比很特殊
select * from student where age>=20 AND age<= 30;
--sql中推荐用and不用&&
select * from student where age between 20 AND 30;
select * from student where age = 22 or age = 19 or age = 25;
--或的用法
select * from student where age in (22,18,25)
--或的第二种用法)
select * from student where english IS(NOT) NULL;
–查询null时需要用is
SELECT * FROM student WHERE NAME LIKE '马%';
–模糊查询 姓马的人 第一个字是马的人
SELECT * FROM student WHERE NAME LIKE'_化%'
;–第二个字是化的人
SELECT * FROM student WHERE NAME LIKE'___'
--姓名是三个字的人
SELECT * FROM student WHERE NAME LIKE '%马%'
--名字中含有马的人
SELECT * FROM student WHERE NAME LIKE '%马';
–模糊查询 最后一个字是马的人
比较运算符 | 说明 |
---|---|
>、 <、 <=、 >=、 =、 <> <> | 在 SQL 中表示不等于,在 mysql 中也可以使用!=没有== |
IN(集合) | 集合表示多个值,使用逗号分隔 |
LIKE’张%’ | 模糊查询 |
IS NULL | 查询某一列为 NULL 的值,注:不能写=NULL |
逻辑运算符 | 说明 |
---|---|
and 或 && 与 | SQL 中建议使用前者,后者并不通用 |
or 或 | 或 |
not 或 ! | 非 |
****视频中提到的例子,在文档中也有****
CREATE TABLE student (
id INT, -- 编号
NAME VARCHAR(20), -- 姓名
age INT, -- 年龄
sex VARCHAR(5), -- 性别
address VARCHAR(100), -- 地址
math INT, -- 数学
english INT -- 英语
);
INSERT INTO student(id,NAME,age,sex,address,math,english) VALUES (1,'马云',55,'男','
杭州',66,78),(2,'马化腾',45,'女','深圳',98,87),(3,'马景涛',55,'男','香港',56,77),(4,'柳岩
',20,'女','湖南',76,65),(5,'柳青',20,'男','湖南',86,NULL),(6,'刘德华',57,'男','香港
',99,99),(7,'马德',22,'女','香港',99,99),(8,'德玛西亚',18,'男','南京',56,65);
排序查询
select * from student order by math
--自动升序排列
select * from student order by math ASC/DESC;
--ASC升序 DESC降序
select * from student order by math ASC,english ASC ;
--组合排序:当第一排序条件相同时使用第二排序条件
聚合函数:将一列数据作为一个整体进行纵向计算,自动排除null
select count(name) from student;
– count:计算个数
select count(ifnull(english,0))from student;
--如果有人成绩为null可以把成绩改0计算
select count(*) from student;
--只要有一行不全为null,进行求和
select max(math) from student; -max:
计算最大值
select min(math) from student;
--min:计算最小值
select sum(math) from student;-- sum:
计算和
select avg(math) from student;--avg:
计算平均值
一般会选择不包含非空的列进行计算:主键
SQL 中的聚合函数 | 作用 |
---|---|
max(列名) | 求这一列的最大值 |
min(列名) | 求这一列的最小值 |
avg(列名) | 求这一列的平均值 |
count(列名) | 统计这一列有多少条记录 |
sum(列名) | 对这一列求总和 |
select sex,avg(math) from student group by sex;
--按照男女分组比较平均分
分组后select加分组后的字段或加聚合函数,加其他没有意义,一般分组会跟聚合函数一起使用
select sex,avg(math) from student WHERE math>70 group by sex;
–按照性别分组,70分以下的不参与分组
select sex,avg(math) from student WHERE math>70 group by sex having count(id)>2;
--分数低于70分的人,不参与分组,分组之后,人数要大于2个人
当我们使用某个字段分组,在查询的时候也需要将这个字段查询出来,否则看不到数据属于哪组的
where 和 having 的区别 | |
---|---|
where 子句 | 1) 对查询结果进行分组前, 将不符合 where 条件的行去掉, 即在分组之前过滤数据, 即先过滤再分组。 2) where 后面不可以使用聚合函数 |
having 子句 | 1) having 子句的作用是筛选满足条件的组,即在分组之后过滤数据,即先分组再过滤。 2) having 后面可以使用聚合函数 |
select product,sum(price)from orders group by product where sum(price) > 30;(×)
--group by 后面不能出现 where,使用 having
SELECT *|字段列表 [as 别名] FROM 表名 [WHERE 子句] [GROUP BY 子句][HAVING 子句][ORDER BY 子句][LIMIT 子句];
–进行分页查询的代码,limit进行分页操作只能用于MySQL
select * from student limit 0,3;
– 第一页从0开始显示三条数据
select * from student limit 3,3;
– 第二页接着显示三条数据
select * from student limit 6,3;
开始的索引 = (当前的页码 - 1) * 每页显示的条数
约束名 | 约束关键字 |
---|---|
主键 | primary key |
唯一 | unique |
非空 | not null |
外键 | foreign key |
检查约束 | check 注: mysql 不支持 |
非空约束 not null
create table stu(id int, name varchar(20) NOT NULL) --name 非空
alter table stu modify name varchar(20); --删除非空约束
alter table stu modify name varchar(20) NOT NULL; --创建表之后添加非空约束
唯一约束 unique :值不能重复
create table stu(id int, phone_number varchar(20) unique ) -
-创建表时添加唯一约束,比如手机号
ALTER TABLE stu MODIFY phone_number VARCHAR(20) UNIQUE;
--删除唯一约束
唯一约束中有两个NULL不会报错
ALTER TABLE stu MODIFY phone_number VARCHAR(20) UNIQUE;
--创建表后添加唯一约束
主键约束 primary key 非空且唯一
一张表只能有一个字段为主键,主键是表中记录的唯一标识,通常不用业务字段作为主键,单独给每张表设计一个 id 的字段,把 id 作为主键。 主键是给数据库和程序使用的,不是给最终的客户使用的。所以主键有没有含义没有关系,只要不重复,非空就行。
create table stu(id int primary key ,name varchar(20));
--在创建表时添加主键
alter table stu drop primary key;
--删除主键
alter table stu modify id int primary key;
--创建表后添加主键
create table stu(id int primary key auto_increment,name varchar(20));
create table stu(id int primary key auto_increment,name varchar(20)auto_increment = 1000;);
–指定起始值alter table st4 auto_increment = 2000
--创建表后更改起始值insert into stu values(null,'aaa');
--如果主键为null,则值按照上一行的数进行自增长alter table stu modify id int;
--删除自动增长。DELETE: 删除所有的记录之后,自增长没有影响。TRUNCATE:删除以后,自增长又重新开始。alter table stu modify id int auto_increment;
--添加自动增长自动增长通常和主键配合使用
疑问:如果一个字段设置了非空与唯一约束,该字段与主键的区别? |
---|
1) 主键数在一个表中,只能有一个。不能出现多个主键。主键可以单列,也可以是多列。 |
2) 自增长只能用在主键上 |
create table 表名 (... constraint 外键名称 foreign key 外键列名称 references 主表名称(主表列名称));
--在创建表时添加外键CONSTRAINT emp_dept_id FOREIGN KEY (dep_id) REFERENCES department(id);
--创建外键约束alter table employee drop foreign key 外键名称
--删除外键alter table 表名 add CONSTRAINT emp_dept_id FOREIGN KEY (外键字段名称) REFERENCES 主表名称(主表列名称));
--在创建表之后添加外键ALTER TABLE employee ADD CONSTRAINT emp_dept_id FOREIGN KEY (dep_id) REFERENCES department(id) ON UPDATE CASCADE;
--创建级联ALTER TABLE employee ADD CONSTRAINT emp_dept_id FOREIGN KEY (dep_id) REFERENCES department(id) ON delete CASCADE;
级联操作语法 | 描述 |
---|---|
ON UPDATE CASCADE 级联更新 | 只能是创建表的时候创建级联关系。更新主表中的主键, 从表中的外键列也自动同步更新 |
ON DELETE CASCADE | 级联删除 |
案例
CREATE TABLE emp (
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(30),
age INT,
dep_name VARCHAR(30),
dep_location VARCHAR(30)
);
-- 添加数据
INSERT INTO emp (NAME, age, dep_name, dep_location) VALUES ('张三', 20, '研发部', '广州');
INSERT INTO emp (NAME, age, dep_name, dep_location) VALUES ('李四', 21, '研发部', '广州');
INSERT INTO emp (NAME, age, dep_name, dep_location) VALUES ('王五', 20, '研发部', '广州');
INSERT INTO emp (NAME, age, dep_name, dep_location) VALUES ('老王', 20, '销售部', '深圳');
INSERT INTO emp (NAME, age, dep_name, dep_location) VALUES ('大王', 22, '销售部', '深圳');
INSERT INTO emp (NAME, age, dep_name, dep_location) VALUES ('小王', 18, '销售部', '深圳');
上方数据会出现数据冗余,需要将数据拆成两张表
-- 解决方案:分成 2 张表
-- 创建部门表(id,dep_name,dep_location)
-- 一方,主表
create table department(
id int primary key auto_increment,
dep_name varchar(20),
dep_location varchar(20)
);
-- 创建员工表(id,name,age,dep_id)
-- 多方,从表
create table employee(
id int primary key auto_increment,
name varchar(20),
age int,
dep_id int -- 外键对应主表的主键
)
-- 添加 2 个部门
insert into department values(null, '研发部','广州'),(null, '销售部', '深圳');
select * from department;
-- 添加员工,dep_id 表示员工所在的部门
INSERT INTO employee (NAME, age, dep_id) VALUES ('张三', 20, 1);
表与表之间的三种关系 |
---|
一对多:最常用的关系 部门和员工 |
多对多:学生选课表 和 学生表, 一门课程可以有多个学生选择,一个学生选择多门课程 |
一对一:相对使用比较少。员工表 简历表, 公民表 护照表 |
1)一对多
一对多(1:n) 例如:班级和学生,部门和员工,客户和订单,分类和商品
一对多建表原则: 在从表(多方)创建一个字段,字段作为外键指向主表(一方)的主键
2)多对多
多对多(m:n) 例如:老师和学生,学生和课程,用户和角色
多对多关系建表原则: 需要创建第三张表,中间表中至少两个字段,这两个字段分别作为外键指向各自一方的
主键。
3)一对一
一对一(1:1) 在实际的开发中应用不多.因为一对一可以创建成一张表。
两种建表原则
1. 第一范式(1NF):每一列都是不可分割的原子数据项
2. 第二范式(2NF):在1NF的基础上,非码属性必须完全依赖于码(在1NF基础上消除非主属性对主码的部分函数依赖)
* 几个概念:
1. 函数依赖:A-->B,如果通过A属性(属性组)的值,可以确定唯一B属性的值。则称B依赖于A
例如:学号-->姓名。 (学号,课程名称) --> 分数
2. 完全函数依赖:A-->B, 如果A是一个属性组,则B属性值得确定需要依赖于A属性组中所有的属性值。
例如:(学号,课程名称) --> 分数
3. 部分函数依赖:A-->B, 如果A是一个属性组,则B属性值得确定只需要依赖于A属性组中某一些值即可。
例如:(学号,课程名称) -- > 姓名
4. 传递函数依赖:A-->B, B -- >C . 如果通过A属性(属性组)的值,可以确定唯一B属性的值,在通过B属性(属性组)的值可以确定唯一C属性的值,则称 C 传递函数依赖于A
例如:学号-->系名,系名-->系主任
5. 码:如果在一张表中,一个属性或属性组,被其他所有属性所完全依赖,则称这个属性(属性组)为该表的码
例如:该表中码为:(学号,课程名称)
* 主属性:码属性组中的所有属性
* 非主属性:除过码属性组的属性
3. 第三范式(3NF):在2NF基础上,任何非主属性不依赖于其它非主属性(在2NF基础上消除传递依赖)
范式 | 特点 |
---|---|
1NF | 原子性:表中每列不可再拆分。 |
2NF | 表中的每一列都完全依赖于主键,不产生局部依赖,一张表只描述一件事情 |
3NF | 不产生传递依赖,表中每一列都直接依赖于主键。而不是通过其它列间接依赖于主键。 |
* 有两个集合A,B .取这两个集合的所有组成情况。
* 要完成多表查询,需要消除无用的数据
1)隐式内连接:使用where条件消除无用数据
SELECT 字段名 FROM 左表, 右表 WHERE 条件
–查询语法
select * from emp,dept where emp,'dept_id'='dept_id'
–查询所有员工信息和对应的部门信息
select emp.name,emp.gender,dept.name from emp,dept where emp.dept_id = dept.id;
– 查询员工表的名称,性别,部门表的名称
SELECT t1.name, --员工表的姓名 t1.gender, --员工表的性别 t2.name --部门表的名称 FROM emp t1, dept t2 WHERE t1.dept_id = t2.id;
--规范书写,方便加注释,为了使排版紧凑,将代码都放在了一行中
2)显式内连接:
SELECT 字段名 FROM 左表 [INNER] JOIN 右表 ON 条件
–查询语法
select 字段列表 from 表名1 [inner] join
表名2 on 条件 --语法
SELECT * FROM emp INNER JOIN dept ON emp.dept_id = dept.id;
- -inner可省略
内连接查询的思维逻辑: 总结内连接查询步骤:1) 确定查询哪些表2) 确定表连接的条件3) 确定查询的条件4) 确定查询的字段
1)左外链接:在内链接的基础上保证
select 字段列表 from 表1 left [outer] join 表2 on 条件;
SELECT t1.*,t2.
nameFROM emp t1 LEFT JOIN dept t2 ON t1.
dept_id= t2.
id;
– 查询所有员工信息,如果员工有部门,则查询部门名称,没有部门,则不显示部门名称
2)右外链接:查询的是右表所有数据以及其交集的部分
select 字段列表 from 表1 right [outer] join 表2 on 条件;
SELECT * FROM dept t2 RIGHT JOIN emp t1 ON t1.
dept_id= t2.id;
–同上
·
SELECT MAX(salary) FROM emp;
--查询最高的工资是多少?
SELECT * FROM emp WHERE emp.
salary= 9000;
查询员工信息,原来需要两条语句
SELECT * FROM emp WHERE emp.
salary= (SELECT MAX(salary) FROM emp);
–查询工资最高的员工信息
–子查询的不同情况
1.查询结果是单行单列的
SELECT * FROM emp WHERE emp.salary < (SELECT AVG(salary) FROM emp); --工资小于平均工资的人
2.查询结果是多行单列的
SELECT id FROM dept WHERE NAME = ‘财务部’ OR NAME = ‘市场部’;
SELECT * FROM emp WHERE dept_id = 3 OR dept_id = 2;-- 查询’财务部’和’市场部’所有的员工信息
SELECT * FROM emp WHERE dept_id IN (SELECT id FROM dept WHERE NAME = ‘财务部’ OR NAME = ‘市场部’);–用一条sql语句写
3.查询结果是多行多列的
子查询可以作为一张虚拟表参与查询
SELECT * FROM dept t1 ,(SELECT * FROM emp WHERE emp.
join_date> '2011-11-11') t2
– 查询员工入职日期是2011-11-11日之后的员工信息和部门信息
WHERE t1.id = t2.dept_id;
– 子查询
SELECT * FROM emp t1,dept t2 WHERE t1.
dept_id= t2.
idAND t1.
join_date> '2011-11-11'
– 普通内连接
– 1) 查询最高工资是多少
select max(salary) from emp;
– 2) 根据最高工资到员工表查询到对应的员工信息
select * from emp where salary = (select max(salary) from emp);
子查询结果是单例多行,结果集类似于一个数组,父查询使用 IN 运算符
需求:查询工资大于 5000 的员工,来自于哪些部门的名字
select dept_id from emp where salary > 5000; -- 先查询大于 5000 的员工所在的部门 id
– 再查询在这些部门 id 中部门的名字 Subquery returns more than 1 row
select name from dept where id = (select dept_id from emp where salary > 5000); select name from dept where id in (select dept_id from emp where salary > 5000);
--视频中的案例
# 创建部门表
create table dept(
id int primary key auto_increment,
name varchar(20)
)
insert into dept (name) values ('开发部'),('市场部'),('财务部');
# 创建员工表
create table emp (
id int primary key auto_increment,
name varchar(10),
gender char(1), -- 性别
salary double, -- 工资
join_date date, -- 入职日期
dept_id int,
foreign key (dept_id) references dept(id) -- 外键,关联部门表(部门表的主键)
)
insert into emp(name,gender,salary,join_date,dept_id) values('孙悟空','男
',7200,'2013-02-24',1);
insert into emp(name,gender,salary,join_date,dept_id) values('猪八戒','男
',3600,'2010-12-02',2);
3 / 30
insert into emp(name,gender,salary,join_date,dept_id) values('唐僧','男',9000,'2008-
08-08',2);
insert into emp(name,gender,salary,join_date,dept_id) values('白骨精','女
',5000,'2015-10-07',3);
insert into emp(name,gender,salary,join_date,dept_id) values('蜘蛛精','女
',4500,'2011-03-14',1);
1.概念 如果一个包含多个步骤的业务操作,被事务管理,那么这些操作要么同时成功,要么同时失败。在实际的开发过程中,一个业务操作如:转账,往往是要多次访问数据库才能完成的。转
账是一个用户扣钱,另一个用户加钱。如果其中有一条 SQL 语句出现异常,这条 SQL 就可能执行失败。事务执行是一个整体,所有的 SQL 语句都必须执行成功。如果其中有 1 条 SQL 语句出现异常,则所有的SQL 语句都要回滚,整个业务执行失败。
2.操作:
开启事务:start transaction;
回滚:rollback;
提交:commit;
3.案例演示 1:事务提交
模拟张三给李四转 500 元钱(成功) 目前数据库数据如下:
使用 DOS 控制台进入 MySQL
执行以下 SQL 语句: 1.开启事务, 2.张三账号-500, 3.李四账号+500
使用 SQLYog 查看数据库:发现数据并没有改变
在控制台执行 commit 提交事务:
使用 SQLYog 查看数据库:发现数据改变
-- 0. 开启事务
START TRANSACTION;
-- 1. 张三账户 -500
UPDATE account SET balance = balance - 500 WHERE NAME = 'zhangsan';
-- 2. 李四账户 +500
-- 出错了...
UPDATE account SET balance = balance + 500 WHERE NAME = 'lisi';
-- 发现执行没有问题,提交事务
COMMIT;
-- 发现出问题了,回滚事务
ROLLBACK;
回滚点的操作语句 | 语句 |
---|---|
设置回滚点 | savepoint 名字 |
回到回滚点 | rollback to 名字 |
1. 原子性:是不可分割的最小操作单位,要么同时成功,要么同时失败。
2. 持久性:当事务提交或回滚后,数据库会持久化的保存数据。
3. 隔离性:多个事务之间。相互独立。
4. 一致性:事务操作前后,数据总量不变
… …
未完待续