MySql单表和约束和事务
代码--------------------------------
/*
排序
使用 order by子句,可以写在分组后面,即可以看出前面的所有操作是一个操作,通常放在最后面
语法结构: select 字段名 from 表名 [where 字段名 = 值] order by 字段名称 [ASC/DESC]
ASC 升序排序 (默认升序)
DESC 降序排序
mysql的字符集来进行排序
*/
-- 对应的表在上一章博客
-- 单列排序 按照某一个字段进行排序
-- 使用salary 字段 对emp表进行排序
SELECT * FROM emp ORDER BY salary; -- 默认升序
SELECT * FROM emp ORDER BY salary DESC; -- 降序排序
-- 组合排序 同时对多个字段进行排序
-- 在薪资的排序基础上,再去使用 id字段进行排序
SELECT * FROM emp ORDER BY salary DESC ,eid DESC;
-- 组合排序的特点: 如果第一个字段 值相同,就按照第二个字段进行排序.
-- 注意:如果所有的排列都相同,那么按照表的顺序来排列,并且对比的序需要看类型,如果是整型,如int,那么看大小,如果是varchar,即字符串,那么看类似的码值,如34>1234
/*
聚合函数
作用:将一列数据作为一个整体,进行纵向的计算的
常用的聚合函数
count(字段) 统计记录数
sum(字段) 求和操作
max(字段) 求最大值
min(字段) 求最小值
avg(字段) 求平均值
都当作字段名,可以用as
除了count可以用*外,其他都不可以用*,因为都是求值
所以对*不可识别,即不可求值,因为不知道对哪个字段求值,只能有数字(包括小数)或字段,通常用字段
但是,对应字段的值也必须要可以识别的,即必须为数字(包括小数),因为要求值
否则会忽略,若全部忽略,则值为0
而count可以用*,是因为他统计记录的,*对count则代表所有记录了,所以可以识别,即可以求值
注意:若对上述函数在查询时进行大小判断,大于(为true的情况下)则返回1(一般代表条数,返回true了),否则返回0
语法格式
select 聚合函数(字段名) from 表名 [where 条件]
*/
#1 查询员工的总数
SELECT COUNT(*) FROM emp;
SELECT COUNT(1) FROM emp;
SELECT COUNT(eid) FROM emp;
-- count函数 在统计的时候会忽略空值
-- 注意 不要使用带空值的列 进行 count,否则可能忽略空值
SELECT COUNT(dept_name) FROM emp; -- 可以这样COUNT(DISTINCT dept_name),加一个去重也行,一般可以对字段操作的,基本上任何场合都行的
#2 查看员工总薪水、最高薪水、最小薪水、薪水的平均值
SELECT
SUM(salary) AS '总薪水',
MAX(salary) '最高薪水',
MIN(salary) '最小薪水',
AVG(salary) '平均薪水'
FROM emp
#3 查询薪水大于4000员工的个数
SELECT COUNT(*) FROM emp WHERE salary > 4000;
#4 查询部门为'教学部'的所有员工的个数
SELECT COUNT(*) FROM emp WHERE dept_name = '教学部';
#5 查询部门为'市场部'所有员工的平均薪水
SELECT AVG(salary) FROM emp WHERE dept_name = '市场部';
/*
分组查询 使用 group by 子句,默认分组的结果会按照对应分组字段进行排序,默认升序,而显示出来的那一个就是其分组数据的分组表的第一个排序的结果
语法格式: select 分组字段/聚合函数 from 表名 group by 分组字段
select后面需要指定的是分组字段,否则可能执行不了
*/
SELECT * FROM emp GROUP BY sex;
# 通过性别字段 进行分组,求各组的平均薪资
SELECT sex, AVG(salary) FROM emp GROUP BY sex;
#1.查询所有部门信息
SELECT dept_name AS '部门名称' FROM emp GROUP BY dept_name;
#2.查询每个部门的平均薪资
SELECT dept_name,AVG(salary) FROM emp GROUP BY dept_name;
#3.查询每个部门的平均薪资,部门名称不能为null
SELECT
dept_name AS '部门名称',
AVG(salary) AS '部门平均薪资'
FROM emp
WHERE dept_name IS NOT NULL
GROUP BY dept_name;
# 查询平均薪资大于6000的部门
-- 1. 首先要分组求平均薪资
-- 2. 求出 平均薪资大于 6000的部门
-- 在分组之后 进行条件过滤 使用: having 判断条件
SELECT
dept_name,
AVG(salary)
FROM emp
WHERE dept_name IS NOT NULL GROUP BY dept_name
HAVING AVG(salary) > 6000;
/*
where 与 having的区别
where
1.在分组前进行过滤
2.where后面不能跟 聚合函数
having
1.是在分组后进行条件过滤
2.having 后面可以写 聚合函数
*/
/*
limit 通过limit 去指定要查询的数据的条数 行数
语法格式
select 字段 from 表名 limit offset, length;
参数说明:
offset: 起始行数 默认从0 开始计数
length: 返回的行数 (要查询几条数据)
*/
# 查询emp表中的前 5条数据
SELECT * FROM emp LIMIT 0,5;
SELECT * FROM emp LIMIT 5;
# 查询emp表中 从第4条开始,查询6条
SELECT * FROM emp LIMIT 3 , 6;
-- limit 分页操作, 每页显示3条
SELECT * FROM emp LIMIT 0,3; -- 第一页
SELECT * FROM emp LIMIT 3,3; -- 第二页
SELECT * FROM emp LIMIT 6,3; -- 第三页 3-1=2 2*3=6
-- 分页公式 起始行数 = (当前页码 - 1) * 每页显示条数
-- order by 英文意思:排序依据,写字段前面,说明对字段排序
-- 后续可以增加多个字段,只能写一次order by,且每个字段后面都可以加上排序规则,sql规定这样写
-- asc 英文意思:升序,默认这个
-- desc 英文意思:降序
-- 在进行排序时,只有一个字段排序时,若有相同的,则按照原来表的顺序
-- 若有两个字段排序,先按照写在前面的排序规则进行排序,然后若有相同的,则按照后面的排序规则进行排序
-- 若再有相同的,则按照原来表的顺序排序,以此类推
-- 即对于多个排序,当排序规则排序完后,若剩下的还有相同的,则按照原来表的顺序排序
-- count 英文意思:总数,count(),括号里必须有数,可以写字段,*,数字
-- 查询时当作字段名,可以用as来改变字段名
-- 字段和*和数字(包括小数),都代表着查询表有多少条记录
-- 其中用字段的话,若字段里有空的,即null,则不会当作一条记录,会忽略空值,即可能会少于用*和数字(包括小数)的
-- 当然了,可以用条件查询来查询符合条件的结果
-- group by 英文意思:分组依据,写表后面,表对应字段前面
-- 且where在分组前面,查询时,查询每个分组的第一条数据,或者就是相同的,就是"分组"而已
-- 注意:where后面必须有条件操作(除非不写,可以直接分组,而不写where),否则报错,如where group by 字段名,where没有条件操作,即报错
-- 这时就可以通过查询条件来操作好的结果,如聚合函数的平均值,会计算每个分组的平均值,若没有使用分组的话
-- 那么,在查询时,规定性别(或其他字段),平均值
-- 那么性别(或其他字段)则会拿第一条数据,进行处理,使得平均值可以出现
-- 当然了,操作的是分组的,就跟操作条件查询后符合条件的数据一样,但分组却可以操作不同字段对应的值(如男和女)
-- 而条件基本不可以
-- 当然,条件基本上达不到分组的效果,要想达到,需要数值来确定(刚好操作,或者观察数值来操作),但这样不严谨
-- 因为分组是将同一种值变成一个记录,方便观察,即在统计的时候,会多用,如统计男性的所有工资和或平均值
-- 而条件要想将多个同一种值变成一个记录,基本不可能,除非用数值来确定(刚好操作,或者观察数值来操作)
-- 注意:对于分组和条件,可以看成先分组(条件)操作后的数据的操作(如查询),其中条件不可以使用聚合函数
-- 因为在条件里,只能对字段进行判断,而不能将字段的值进行操作判断
-- 所以基本上聚合函数是操作查询符合条件后的数据
-- 之所以是基本上,是因为分组可以在分组后进行聚合函数操作的判断
-- 如having,因为分组后,可以将分组看成一个字段,对字段进行判断,相当于对所有字段的值
-- 因为查询时,一个分组,就一条数据进行判断
-- 与条件不同,条件是先判断(内存里进行过滤)再查询数据
-- 对于想象来说可以理解为最后一次查询给你看,实际上已经操作了表数据
-- 可以想象成先将数据查询好,再过滤,然后将过滤的查询给你看
-- 而这里是先分组,再判断(内存里进行过滤)
-- 然后查询分组的数据(对于想象来说可以理解为最后一次查询,有条件在,即可以有先判断再分组)
-- 可以想象成先将分组查询好,再过滤,然后将过滤的查询给你看
-- 最后查询的结果是对应的分组的第一条数据,然后将操作的数据放在查询的第一条数据的对应位置
-- 对于分组后的每一个分组都可以看成一个表
-- 所以分组的聚合函数,是操作分组后的所有分组(可以看成表),而条件判断后的聚合函数则操作判断后的所有数据
-- 这样的聚合函数一般都写在select后面,即查询后面
-- 查询出来的是每个分组的第一条数据,可以先对每个分组进行判断
-- 然后聚合函数操作每个分组的所有数据(操作完后,也可以比较大小)
-- 并放在查询的第一条数据的对应位置
-- 而分组和条件都操作时,条件写前面,因为条件优先一点,等条件操作完后,在操作分组
-- 其中进行条件操作时,由于是条件,即只能用逻辑运算符来连接,不可加逗号,如and或者or等等
-- having 英文意思:持有,由于条件和分组一起操作时,条件是先判断条件,然后分组,而having可以在分组后操作
-- 写在分组对应字段后面
-- limit 英文意思:限制,limit 0,5,0可以不写,默认从0位置开始,查询5条,包括0位置的数据
-- 这里注意,是5条,而不是位置,所以不是查询6条,而是5条
-- limit只是限制查询的结果,即只操作最后的查询结果
-- 无论你是分组,还是条件,最后的查询结果,都会受到limit的限制
-- 但limit都是写在这两者的后面的,一般都写在最后面
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/631c2bcd0bec5113c25b09195265c7f3.png#pic_center)
/*
约束
约束是指对数据进行一定的限制,来保证数据的完整性 有效性 正确性
常见的约束
有些是创建索引(not null不是索引),只有自占一行的,才算做索引
只不过实现了约束功能,而自占一行的都有对应的索引名
主键约束 primary key
唯一约束 unique
非空约束 not null
外键约束 foreign key
*/
/*
主键约束
特点 不可重复 唯一 非空
作用 用来表示数据库中的每一条记录
语法格式
字段名 字段类型 primary key
*/
-- 方式1 创建一个带有主键的表
CREATE TABLE emp2(
eid INT PRIMARY KEY,
ename VARCHAR(20),
sex CHAR(1)
);
DESC emp2;
-- 方式2 创建
DROP TABLE emp2; -- 删除表
CREATE TABLE emp2(
eid INT,
ename VARCHAR(20),
sex CHAR(1),
PRIMARY KEY(eid) -- 指定eid为主键
);
-- 方式3 创建表之后 再添加主键
CREATE TABLE emp2(
eid INT,
ename VARCHAR(20),
sex CHAR(1)
);
-- 通过DDL语句 添加主键约束
ALTER TABLE emp2 ADD PRIMARY KEY(eid);
-- 删除主键 DDL语句
ALTER TABLE emp2 DROP PRIMARY KEY;
/*
主键的自增
关键字: auto_increment 主键的自动增长(字段类型必须是 整数类型)
注意:该关键字加上后,可以在信息中,看到该属性
且他这个属性后面的值就是下一个要添加的值,所以信息也能知道他的下一个值是多少(sqlyog图形化界面的信息中)
其他的图形化界面不清楚,但一般都有
注意:在设置自增前,对应的主键不能有存在0这个数,负数可以,否则会添加不了自增操作
而添加自增后,加上0,默认代表没有添加数据,即操作自增的数据
*/
-- 创建主键自增的表
CREATE TABLE emp2(
-- 主键自增
eid INT PRIMARY KEY AUTO_INCREMENT,
ename VARCHAR(20),
sex CHAR(1)
);
-- 添加数据 观察主键变化
INSERT INTO emp2(ename,sex) VALUES('张三','男');
INSERT INTO emp2(ename,sex) VALUES('李四','男');
INSERT INTO emp2 VALUES(NULL,'翠花','女');
INSERT INTO emp2 VALUES(NULL,'艳秋','女');
-- 修改自增起始值
-- 重新创建自增主键的表, 自定义自增的起始位置
CREATE TABLE emp2(
eid INT PRIMARY KEY AUTO_INCREMENT,
ename VARCHAR(20),
sex CHAR(1)
)AUTO_INCREMENT=100;
/*
DELETE和TRUNCATE对自增长的影响
delete 删除表中所有数据, 将表中的数据逐条删除.
truncate 删除表中的所有数据, 是将整个表删除,然后再创建一个结构相同表.
*/
-- delete 方式删除所有数据
DELETE FROM emp2; -- delete 删除对自增是没有影响
INSERT INTO emp2(ename,sex) VALUES('张百万','男'); -- 102
INSERT INTO emp2(ename,sex) VALUES('艳秋','女'); -- 103
-- truncate 删除所有数据
TRUNCATE TABLE emp2; -- 自增从1开始
INSERT INTO emp2(ename,sex) VALUES('张百万','男'); -- 1
INSERT INTO emp2(ename,sex) VALUES('艳秋','女'); -- 2
/*
非空约束
特点 表中某一列不予许为空
语法格式
字段名 字段类型 not null
*/
CREATE TABLE emp2(
eid INT PRIMARY KEY AUTO_INCREMENT,
-- 将 ename字段 添加了 非空约束,自己添加的null也不可以
-- Mysql,再没有其他操作时,创建表,里面数据默认添加的都是null(他不受约束),除非设置默认值(default)
ename VARCHAR(20) NOT NULL,
sex CHAR(1)
);
/*
唯一约束
特点: 表中的某一列不能够重复 (对null值 不做唯一判断)
语法格式
字段名 字段类型 unique
*/
-- 创建 emp3表 为ename 字段添加 唯一约束
CREATE TABLE emp3(
eid INT PRIMARY KEY ,
ename VARCHAR(20) UNIQUE, -- unique unique,后面可以加多个unique不影响,即不报错
-- 因为多次声明,最后的结果还是一个结果,那就是唯一
sex CHAR(1)
-- UNIQUE KEY sd (eid),也可以这样对字段添加唯一约束,sd是约束名(随便写),eid的字段名
);
-- unique与添加主键一样,都是自占一行,但是他不是唯一的,即当你添加多个unique时
-- 如alter table emp3 modify eid inr unique,可以运行多次,只不过第二次的对应约束名变了而已
-- 测试唯一约束
INSERT INTO emp3 VALUES(1,'张百万','女');
-- Duplicate entry '张百万' for key 'ename' 不能重复
INSERT INTO emp3 VALUES(2,'张百万','女');
-- 唯一约束的值 可以为 null
INSERT INTO emp3 VALUES(2,NULL,'女');
-- 主键约束和唯一约束的区别
-- 1.主键约束 他是唯一且不能为空的
-- 2.唯一约束 唯一,但是可以为空
-- 3.一个表中只能有一个主键,但是可以有多个唯一约束
-- 外键约束 多表中再学习
/*
默认值
特点 用来指定 某一列的默认值
语法格式
字段名 字段类型 default 默认值
*/
-- 创建 emp4表, 指定 sex默认为 女
CREATE TABLE emp4(
eid INT PRIMARY KEY,
ename VARCHAR(20),
sex CHAR(1) DEFAULT '女'
);
INSERT INTO emp4(eid,ename) VALUES(1,'杨幂');
INSERT INTO emp4(eid,ename) VALUES(2,'柳岩');
-- 不使用默认值
INSERT INTO emp4(eid,ename,sex) VALUES(3,'蔡徐坤','男');
-- 注意:对于修饰来说,若没有特别注明位置的,都是可以随便放的(需要先满足注明位置的)
-- primary key 英文意思:主关键字
-- 可以看成主键包括唯一约束和非空约束,且还有其他功能,如排列(从小到大,那么相当于进行了升序排列,因为无论是否升序排列,都是升序的效果),但只能有一个主键
-- 主键字段的值不可重复(唯一,只有一个主键),非空,在创建表时,放在字段类型的后面,进行约束
-- 也可以放在单独一行,进行约束,如primary key(字段名)
-- 也可以使用add来添加约束,在修改表结构时(alter),add后面的代码,就相当于在创建表时,进行初始化字段的代码
-- 如add primary key(字段名),也可以删除主键,如drop
-- 在修改表结构时(alter)drop后面的代码,就相当于单独的字段或者其他修饰
-- 如drop(字段名),或者drop primary key,一个删除字段,一个删除主键修饰
-- unique 英文意思:唯一的,对字段进行修饰,写在字段类型后面,但是可以为空(null),对空不做唯一
-- foreign key 英文意思:外键
-- auto_increment 无英文意思,用来使主键自动增长(字段类型必须是整数类型)写在主键修饰的后面,必须要在主键后面
-- 即修饰主键的,没有主键报错,当然后面可以添加其他修饰,如not null
-- 位置与自增修饰可以随便放,只要保证自增在主键后面即可
-- 当你在其他字段(第一条数据除外)添加值(手动添加或者代码添加)时
-- 若忘记在主键的字段位置添加值(或添加null,即空值),则不会报错
-- 若上一条数据存在,则将上一条主键字段值与自增修饰起始值进行比较
-- 若上一条主键字段值大,则将上一条主键字段值赋值给自增修饰起始值
-- 然后自增修饰起始值加1,再赋值给当前主键字段值
-- 若自增修饰起始值大,则自增修饰起始值加1,再赋值给当前主键字段值,这样的就是自增
-- 若是第一条数据(第一次添加的数据),则将自增修饰起始值赋值给当前主键字段值
-- 当然了,可以修改自增修饰起始值,在创建表的后面,即最后的分号和表的括号之间,写上自增修饰
-- 实际上每次操作都会保存自增,即大于对应1的自增的保存,在表消息中可以看到) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8,其中AUTO_INCREMENT=8中的8就是下一次自增的数
-- 如create table 表名(字段的一些操作)auto_increment=100;,将自增修饰起始值改成100
-- 注意:只要表还在,那么自增修饰起始值就不会变
-- 所以手动的删除数据或者代码(delete)删除数据,都不会改变自增修饰起始值
-- 而truncate是删除表,即原来的表删除,那么原来表的自增修饰起始值就删除了
-- 然后创建一个一模一样的,而这个一模一样的表
-- 自增修饰不会添加(不在表的结构里,即表的括号里),即新建的自增修饰起始值为1
-- 当没有主键时,字段值的添加是直接添加到下一条,注意不要忘记刷新
-- 而有主键时,字段的添加会按照主键的从小到大(数字和字母的每个位置都要比较)排列而添加,注意不要忘记刷新
-- 刷新后的才是真正位置
-- default 英文意思:默认,写在字段类型后面,该修饰后面必须先写需要的默认值(必须符合字段类型)
-- 注意若字符串类型里放的是数字,那么也可以给整数类型,小数点和小数点后面的会去掉,与java类似
-- 使得创建表时,该字段不再默认添加null了,而是该默认值
-- 如default '男',默认添加男,也可以自己手动修改,或者代码修改
-- 若代码不对对应字段值添加值时,则添加默认值(不设置默认值时,添加为null,设置默认值时,添加设置的默认值)
-- 创建账户表
CREATE TABLE account(
-- 主键
id INT PRIMARY KEY AUTO_INCREMENT,
-- 姓名
NAME VARCHAR(10),
-- 余额
money DOUBLE
);
-- 添加两个用户
INSERT INTO account (NAME, money) VALUES ('tom', 1000), ('jack', 1000);
-- tom账户 -500元,一般操作update必须要set的操作,如果需要不变可以设置set a=a
UPDATE account SET money = money - 500 WHERE NAME = 'tom';
出错了
-- jack账户 + 500元
UPDATE account SET money = money + 500 WHERE NAME = 'jack';
/*
MySql事务操作(后面的概念比较繁琐,建议最好以锁的概念来理解,当然不理解也没有什么关系,了解即可,以后会说明的)
手动提交事务
1.开启事务 start transaction; 或者 begin;,使用这个那么就不会操作自动提交,否则一般会,除非进行设置,如SET @@autocommit=off;
2.提交事务 commit;
3.回滚事务 rollback;
一般情况下,只要开启事务后,对应的再次的开启事务是没有操作的
所以这时的会开启事务的sql也就相当于没有开启事务了
自动提交事务
MySql默认的提交方式 自动提交事务
每执行一条DML语句 都是一个单独的事务
*/
-- 手动提交事务演示
-- 成功案例
USE db2;
start transaction; -- 开启事务
update account set money = money - 500 where name = 'tom'
update account set money = money + 500 where name = 'jack';
commit; -- 提交事务
-- 失败案例 会进行回滚
start transaction; -- 开启事务
INSERT INTO account VALUES(NULL,'张百万',3000);
INSERT INTO account VALUES(NULL,'有财',3500);
-- 直接关闭窗口 模拟系统崩溃 数据没有发生改变
-- 如果事务中 SQL 语句没有问题,commit 提交事务,没有问题的会对数据库数据的数据进行改变
-- 如果事务中 SQL 语句有问题,commit 提交事务,有问题的不会对数据库数据的数据进行改变
-- 而rollback 回滚事务,无论是否有问题,自然会回退到开启事务时的状态
-- 实际上sql语句发生错误,并不会使得自动提交,除非关闭窗口,或者其他会导致事务结束的情况(可能的某些错误)
-- 因为事务的最终操作是表,即只会看表的状态
-- 而当我们手动开启时,对应的自动提交,就需要手动提交了
-- 自动提交事务 MySQL 默认每一条 DML(增删改)语句都是一个单独的事务,实际上查询也会算的(通常并不是,所以不要认为查询是操作了事务,因为事务是一个操作,而非固定的操作),即每个sql都会默认操作事务(是每个哦),只不过查询并没有改变数据,所以你可能认为他没有事务的发生
-- 登录mysql,查看autocommit状态。
SHOW VARIABLES LIKE 'autocommit';
-- 把 autocommit 改成 off;
SET @@autocommit=off;
-- 执行修改操作
-- 选择数据库
use db2;
-- 修改数据
update account set money = money - 500 where name = 'jack';
-- 手动提交
commit;
/*
事务的四大特性
原子性
每个事务都是一个整体,不可以再拆分,事务中的所有SQL
要么都执行成功 要么都执行失败(因为回滚或者失败报错)
一致性
事务在执行之前 数据库的状态,与事务执行之后的状态要保持一致
隔离性
事务与事务之间不应该相互影响,执行时要保证隔离状态
持久性
一旦事务执行成功,对数据的修改是持久的.
*/
/*
MySql的隔离级别
各个事务之间是隔离,相互独立.但是如果多个事务对数据库中的同一批数据
进行并发访问的时候,就会引发一些问题,可以通过设置不同的隔离级别来解决
对应的问题
并发访问的问题
脏读: 一个事务读取到了另一个事务没有提交的数据.
不可重复读: 一个事务中 两次读取的数据不一致
幻读: 一个事务中,一次查询的结果,无法支撑后续的业务操作.
设置隔离级别
read uncommitted: 读未提交
可以防止哪些问题: 无
read committed: 读已提交 (Oracle默认 隔离级别)
可以防止: 脏读
repeatable read : 可重复读 (MySql默认的隔离级别)
可以防止: 脏读 ,不可重复读
serializable : 串行化
可以防止: 脏读 ,不可重复读 ,幻读
注意: 隔离级别 从小到大 安全性是越来越高的,但是效率是越来越低的,
根据不同的情况选择对应的隔离级别
*/
/*
查看隔离级别
select @@tx_isolation;
设置隔离级别
set global transaction isolation level 级别名称;
read uncommitted 读未提交
read committed 读已提交
repeatable read 可重复读
serializable 串行化
*/
-- 查看隔离级别 MySql默认隔离级别 repeatable read
select @@tx_isolation;
-- 设置隔离级别为 读已提交
set global transaction isolation level read committed;
-- start transaction 英文意思:开启事务,只有当关闭事务时(commit)
-- 开启事务后面的代码才会对数据进行操作,否则不会,即虽然运行成功,但是并没有操作,而是准备操作
-- 而在事务里操作的其实是对日志的操作(这里的说明,注意:只是这里的说明,可能并不是这样的意思,这里只是给出一个好理解的定义而已),当你与当前表数据进行交互时(增删改查)
-- 都会使得日志有最开始数据(当前表数据),实际上也可以使用反向sql栈来表示,但是解释是多样的,最好还是看源码来理解,这里我们以一种特殊的解释来说明,所以可能与源码不符合,即你可以不看
-- 回滚就是返回当前表的数据,而不是日志中最开始的数据
-- 就如其他事务提交时,当前表数据改变,与你日志最开始数据不同,当你回滚时
-- 其实返回当前表的数据,而不是日志中最开始的数据(实际上这里也可以认为是真的修改表数据了)
-- 若报错,则报错部分是无视的,即只有不报错的,才会执行
-- 若直接退出程序(窗口),则不执行代码,这样的操作和直接退出程序简称为事务的回滚
-- 即所有影响到的数据将返回到当前表的数据
-- 而对于查询来说,事务外查询的是当前表的数据
-- 事务内查询的是日志的数据
-- 即这个查询会与当前表数据进行交互(所以在一定程度上,其实日志就是表数据,只是这里以日志交互作为说明),即操作的数据不会显示当前表对应的数据,隔离的查询都会交互
-- 意思是查询表时,将自己操作的数据放在对应位置
-- 实际上在进行DML操作时(增删改),每执行一条,都是有事务的开启和关闭的,只不过省略了而已
-- 这样的叫做自动提交事务,即MySQL默认每一条DML(增删改)语句都是一个单独的事务
-- 当我们将系统变量的autocommit(会话变量),从no修改到off时,那么就不会自动提交了
-- 即必须要commit来提交,否则的话数据是不会真正修改的,而你在提交前的,查询的数据,是你真正修改后的数据
-- 只是他给你看的,若这时你不提交,那么他就会回滚(撤销),即所有影响到的数据将返回到当前表的状态
-- 当有多个dos窗口时,数据是共享的,因为指向同一数据
-- 即其中一个窗口对表操作时,另外一个窗口查到的数据是修改后的数据,即共享数据
-- 下面解释Mysql自带的隔离级别
-- 对于autocommit是会话变量,如果其中一个设为no
-- 另外一个设为off或者开启事务(这个设为off或者开启事务,可以理解为产生了日志,所有隔离都使用这个日志)
-- 而在设为no的dos窗口里操作DML(增删改)时,设为off的dos窗口查询到的数据并没有操作,并不是没有共享
-- 而是设为off的dos窗口查询的是日志里没改变的数据
-- 而当设为off的dos窗口操作DML(增删改)时(所有事务的第一次),就会于当前表数据进行交互,从而日志改变
-- 就有了表数据的作用了,然后设为no的dos窗口就不可以操作DML(增删改)了(其他事务也一样)
-- 而在设为off的dos窗口里操作DML(增删改)时,设为no的dos窗口查询到的数据也并没有操作,并不是没有共享
-- 而是设为off的dos窗口在提交之前,当前表的数据实际不变
-- 所以设为no的dos窗口查询到的数据也就不是设为off的dos窗口查询到的数据了
-- 当两个dos窗口都设为off,使得DML(增删改)都需要手动提交时
-- 若其中一个dos窗口使用DML(增删改)操作时(所有事务的第一次)
-- 另外一个不可以使用DML(增删改)操作(其他事务也一样),这可以看出Mysql的事务有类似与java锁的概念
-- 即其中一个事务使用了DML(增删改)后(所有事务的第一次)
-- 那么另外一个(或多个)事务必须等待他提交(这个提交就相当于释放对应操作的锁)后,才可使用
-- 以此类推,可以有多个dos窗口,也是同样的操作
-- 若使用可视化界面的手动停止,那么就会重新设为no
-- 即事务有隔离性,上述是对于Mysql默认的隔离级别的描述,对于隔离级别,首先说出不隔离的情况
-- 即并发访问的问题
-- 脏读:一个事务读取到了另一个事务没有提交的数据,其他事务回滚后,数据又会变为原来的数据
-- 不可重复读:一个事务中,两次读取的数据不一致,每次读取,都可以被其他事务的操作进行改变
-- 即自己事务查询的数据与自己的需要的数据不对
-- 幻读:一个事务中,一次查询的结果,无法支撑后续的业务操作,通过改变原数据,使得查询结果不同
-- 如查询数据时,其他事务操作完,再提交,使得增删改操作让数据变化,这时查询数据虽然不变
-- 但是操作被提交的事务的数据时,会出现不一致性(修改),或者报错(删除和增加)等等
-- 我们可以设置隔离级别,如set global transaction isolation level 隔离级别名称,在设置完后
-- 需要新建dos窗口,因为设置的就是永久的,但不作用于当前窗口
-- 在事务里,当其中一个事务操作过一条数据时,其他事务不可操作这一条数据,事务自带的锁,但可以操作其他数据
-- 而使用DML(增删改)操作时,必定会与当前表的数据发生交互
-- 即结果也会返回当前表的数据,再记录日志里面,事务内查询的是日志数据,即才会有serializable隔离级别
-- 而repeatable read和serializable的隔离级别不可以操作其他数据,加锁的,其他级别没有加这样的锁
-- 隔离级别有
-- read uncommitted:读未提交,可以读到未提交的
-- 查询数据也会与当前表交互,即操作的数据不会显示当前表对应的数据
-- 放入日志,可以防止哪些问题:无
-- read committed:读已提交 (Oracle默认 隔离级别),不可以读到未提交的,且事务内查询的数据是日志的数据
-- 由于事务内查询的是日志的数据
-- 即这个查询会与当前表数据进行交互,即操作的数据不会显示当前表对应的数据,然后放入日志
-- 使得别人可以操作你没操作过的数据,让当前表数据改变,导致查询时
-- 出现不同结果,可以防止:脏读
-- repeatable read:可重复读(MySql默认的隔离级别)
-- 之所以默认这个,是因为这个在相对安全的情况下,也可以进行最重要的查询
-- 其他事务不可以操作自己的数据,且所有的数据都是操作的数据了,即操作的数据不会显示当前表对应的数据
-- 第一次(其他事务没使用过)使用DML(增删改)开始操作(这里会与当前表数据进行交互)除外
-- 第一次使用DML(增删改)操作完后,其他事务不可操作DML(增删改)
-- 结果返回当前表的数据,操作后,这些操作被锁住
-- 且其他事务不可操作,可以防止:脏读,不可重复读
-- serializable:串行化,一个事务的操作与其他事务无关,无论是操作前还是操作后,都只会与自己日志有关
-- 因为只要开启这个隔离级别时
-- 且谁先与当前表数据进行DML(增删改)操作(交互)
-- 那么其他事务就不可以用DML(增删改)和DQL(查)的操作(不可以与当前表进行交互)
-- 注意:其他事务连DQL(查)操作都不可以有,即表数据被占用,不可被其他事务操作
-- 但是若不使用DML(增删改)操作交互,而使用查询表来交互,那么当一个事务查询表时
-- 另外一个事务进行DML(增删改)操作时
-- 会阻塞,若两个事务都查表了,那么先执行DML(增删改)操作的,会被后者执行DML(增删改)操作使得不阻塞
-- 让前者成功,而后者就会失败,即报错
-- 若其中一个不查询表,那么无论谁先执行,不查询表的都会报错,而查询表的则没有阻塞的执行成功
-- 所以谁先交互多,谁先优势,最后由DML(增删改)决定
-- 类似于java的串行,可以防止:脏读,不可重复读,幻读
-- 注意:隔离级别,从小到大,安全性是越来越高的,但是效率是越来越低的,根据不同的情况选择对应的隔离级别
-- 可以通过select @@tx_isolation查看隔离级别,tx_isolation是会话变量,多个dos的tx_isolation不同
-- 可视化界面(软件)的窗口实际上也是操作dos窗口
-- 复习java,异步同步,并发串行,代表同时操作和等待操作
-- begin 英文意思:开始,也可以开启事务
-- commit 英文意思:保证,提交事务,写在最后,当作结束事务的意思
-- rollback 英文意思:回落,让事务回滚,写在最后,当作结束事务的意思
-- 注意:mysql也是有类似于java的变量的,我们在用set设置时,其实可以看做是设置表的变量(字段名)
-- 那么也会有适用于全部的变量(类似于java的全局变量)
-- 但这个全部的变量却包括了全局变量和会话变量
-- 会话表示不同窗口,即多个dos窗口进入mysql,而全局包括所有的,无论你创建多少个dos窗口)
-- 在sql里使用show variables(有很多类似于这样的代码)
-- 就可以知道所有的系统变量(设置时加上@@表示系统变量(全局和会话),加上@表示用户变量(也可以看成会话,只不过他会消失,没有什么默认的)),这是规定的
-- 就如java方法类似,其中autocommit变量用于对事务的操作,默认为no(修改时,修改为1,可以回来,即变为no)
-- 可以设置让事务不再自动提交(修改为off或者0),而是手动提交,修改了底层的操作
-- 主要用于DML(增删改),可以看出Mysql的一些操作,也是通过类似于java代码的方式进行的,明面上看不到罢了
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/6745a02b86cd363dfba74e739da306b6.png#pic_center)
实际上事务的说明就是锁的说明,因为锁的原因,导致有对应的操作,具体锁的说明,可以去看看96章博客