MySQL_基础篇
- (一)、MySQL 介绍
- 1.MySQL三大阶段
- (1).基础篇
- (2).进阶篇
- (3).运维篇
- 2.MySQL 概念
- 3.数据模型
- (1).关系型数据库(RDBMS)
- 4.数据库三大范式
- (二)、SQL 编程语言
- 1.SQL通用语法
- 2.SQL 四大分类
- 3.DDL (数据定义语言)
- (1).数据库操作 ->(增删改查)
- (2).表操作 -> (增删改查)
- 4.DML (数据操纵语言)
- (1).DML_添加数据
- (2).DMl_修改数据
- (3).DML_删除数据
- 5.DQL (数据查询语言) -执行顺序在这 ⭐
- (1).DQL语法
- (2).DQL_基本查询
- (3).DQL_条件查询
- (4).聚合函数
- (5).DQL_分组查询
- (6).DQL_排序查询
- (7).DQL_分页查询
- (8).DQL_示列
- 6. DCL (数据控制语言)
- (1).DCL 介绍
- (2).DCL 管理用户
- (3).DCL_权限控制
- (三)、函数 - (非聚合函数)
- 1. 字符串函数
- (1).基础知识
- (2).字符串语法
- (3).示列
- 2.数值函数
- (1).基础知识
- (2).示列
- 3.日期函数
- (1).基本知识
- (2).示列
- 4.流程函数
- (1).基本知识
- (2).示列
- (四)、约束
- 1.约束概述
- (1).约束的概念
- (2).约束的目的
- (3).约束的分类
- 2.约束演示
- 3.外键约束
- (1).外键约束的概念
- (2).SQL语句
- (3).外键约束的语法
- (4).有外键约束与外键约束情况
- 4.外键删除更新行为
- (1).删除/更新行为
- (2).修改删除/更新行为语法
- (3).级联删除/更新测试
- (4). set null 删除/更新测试
- (五)、多表查询
- 1.多表关系
- (1).多表查询概述
- (2).一对多 (多对一)关系
- (3).多对多关系
- (4)一对一关系
- 2.多表查询概述
- (1).笛卡尔积效应
- (2).消除笛卡尔积效应
- 3.多表查询分类
- (1).连接查询
- (2).子查询
- 4.连接查询-内连接 (两张表交集部分) ⭐
- (1).隐式内连接语法
- (2).显示内连接语法
- (3).隐式内连接和显示内联接测试
- 5.连接查询-外连接 (查询某张表的全部数据和交集部分)
- (1).外连接查询语法
- (2).左外连接测试
- (3).右外连接测试
- 6.连接查询-自连接 (可交集、可单独) ⭐
- (1).自连接查询语法
- (2).自连接测试
- 7.联合查询-union,union all
- (1).联合查询的语法
- (2).联合查询示列
- 8.子查询 (嵌套查询) ⭐
- (1).子查询的基本语法
- (2).子查询的分类
- 9.子查询-标量子查询 (返回结果是一个值)
- (1).标量子查询
- (2).标量子查询示列
- 10.子查询-列子查询 (单列但可多行)
- (1).列子查询
- (2).列子查询示列
- 11.子查询-行子查询 (单行但可多列)
- (1).行子查询
- (2).行子查询示列
- 12.子查询-表子查询 (多行且多列)
- (1).表子查询
- (2).表子查询示列
- (六)、事务
- 1.事务简介
- 2.事务操作
- (1).未出现异常下的事务
- (2).出现异常下的事务
- 3.事务控制 - (第一种方法)
- (1).事务管理方法
- (2).事务提交示列
- (3).事务回滚示列
- 4.事务控制 -(第二种方法)
- (1).事务方法
- (2).事务控制
- 5.事务四大特性 (ACID)
- (1).四大特性
- 6.并发事务问题
- (1).并发事务引起的三大问题
- (2).三大问题详细介绍
- 7.事务隔离级别
- (1).事务隔离级别
- (2).查看/操作事务隔离级别
- (3).演示脏读
- (4).演示不可重复读
- (5).演示幻读
(一)、MySQL 介绍
1.MySQL三大阶段
(1).基础篇
- MySQL概述
- SQL
- 函数
- 约束
- 多表查询
- 事务
(2).进阶篇
- 存储引擎
- 索引
- SQL优化
- 视图/存储过程/触发器
- 锁
- InnoDB核心
- MySQL管理
(3).运维篇
- 日志
- 主从复制
- 分库分表
- 读写分离
2.MySQL 概念
- 数据库(DB): 存储数据的仓库,数据是有组织的进行存储。
- 数据库管理系统(DBMS): 操纵和管理数据库的
大型软件
。 - SQL: 操纵关系型数据库的
编程语言
,定义了一套操作关系型数据库统一标准。
3.数据模型
(1).关系型数据库(RDBMS)
概念: 建立在关系模型基础上,由多张相互链接的二维表
组成的数据库。
特点:
- 使用表存储数据,格式统一,便于维护。
- 使用SQL语言操作,标准统一,使用方便。
我们的电脑上安装完MySQL之后,我们的电脑就会自动成为MySQL数据库的服务器,在服务器上会存储我们客户端通过SQL语句编译的数据。
4.数据库三大范式
- 第一范式: 属性(字段)不能再分,也就是说字段不能是集合、数组。
- 第二范式:首先满足第一范式,其次不能存在部分依赖。也就是说非主键字段必须完全依赖于主键。(也就是说只有一个主键)
- 第三范式:首先满足第二范式,其次不能存在依赖传递。也就是说不能非主键字段依赖于另一个非主键字段。
(二)、SQL 编程语言
1.SQL通用语法
- SQL语句可以单行或多行书写,以
分号结尾
。 - SQL语句可以使用
空格缩进
来增强语句的可读性。 - MySQL数据库的SQL语句
不区分大小写
,关键字建议使用大写。 - 注释:
- 单行注释:
- -
注释内容 或#
注释内容(MySQL特有) - 多行注释:
/ * 注释内容 * /
- 单行注释:
2.SQL 四大分类
Data Definition Language(DDL)
:数据定义语言,用来定义数据库对象(数据库,表,字段)。Data Manipulation Language(DML)
: 数据操纵语言,用来对数据库表中的数据进行增删改。Data Query Labguage(DQL)
: 数据查询语言,用来查询数据库中表的记录。Data Control Language(DCL)
: 数据控制语言,用来创建数据库用户、控制数据库的访问权限。
3.DDL (数据定义语言)
(1).数据库操作 ->(增删改查)
1.查询
show databases; #查询所有的数据库
select database(); #查询当前数据库
2. 创建
create database [if not exists] 数据库名 [default charset 字符集] [collate 排序规则]; #假如不存在这个数据库我们就进行创建
3.删除
drop database [if exists] 数据库名; # 假如存在这个数据库就删除
4. 使用
use 数据库名; # 使用我们指定的数据库
(2).表操作 -> (增删改查)
- DDL_表操作_查询
1. 查询当前数据库所有表
show tables; # 展示所有的表
2.查询表结果
DESC 表名;
3.查询指定表的建表语句
show create table 表名;
- DDL_表操作_创建
1.表的创建
create table 表名(
字段1 字段1类型 [comment '字段1注释'],
字段2 字段1类型 [comment '字段2注释'],
字段3 字段3类型 [comment '字段3注释']
)[comment '表注释'];
create table tb_user(
id int(4) comment '编号',
name varchar(20) comment '姓名',
age int(4) comment '年龄',
gender varchar(4) comment '性别'
) comment '用户表';
- DDL_表操作_添加
1.给表中添加字段
alter table 表名 add 字段名 字段类型(长度) [comment 注释] [约束];
- DDL_表操作_修改
1.修改数据类型
alter table 表名 modify 字段名 新数据类型(长度);
2.修改字段名和字段类型
alter table 表名 change 旧字段名 新字段名 新/旧类型(长度) [comment 注释] [约束];
3.修改表名
alter table 表名 rename to 新表名;
- DDL_表操作_删除
1.删除字段
alter table 表名 drop 字段名;
2.删除表,数据和表结构都删除
drop table[if exists] 表名;
3.删除指定表,并重新创建该表
truncate table 表名;
4.DML (数据操纵语言)
DML英文全称是 Data Manipulation language
(数据操纵语言),用来对数据库中的数据记录进行增删改操作。
- 添加操作 (insert)
- 修改操作(update)
- 删除操作(delete)
(1).DML_添加数据
- 给指定字段添加数据
insert into (字段名1,字段名2...) values(值1,值2...); # 给字段添加数据
- 给全部字段添加数据
insert into 表名 values(值1,值2...);
- 批量添加数据
insert into 表名(字段1,字段2...) values(值1,值2...),(值1,值2...)
insert into 表名 values(值1,值2...),(值1,值2...),(值1,值2...);
注意:
- 插入数据时,指定的字段顺序需要与值得顺序是一一对应的。
- 字符串和日期型数据应该包含在引号中。
- 插入的数据大小,应该在字段的规定范围内。
(2).DMl_修改数据
1.多个字段数据修改
update 表名 set 字段1=值1, 字段名2=值2, ... [where 条件];
2.单个字段数据修改
update 表名 set 字段1=值1 [where 条件];
注意:
- 如果不加where条件的话,默认会修改整个表的数据。
- 如果加了where条件的话,默认只会修改单个字段的数据。
(3).DML_删除数据
delete from 表名 [where 条件]
注意:
- delete语句的条件可以有,也可以没有,如果没有的话,则会删除整张表的所有数据。
- delete 语句不能删除某一个字段的值(可以使用update)
5.DQL (数据查询语言) -执行顺序在这 ⭐
DQL 英文全称是Data Query Language(数据查询语言),数据查询语言,用来查询数据库中表的记录。
/*
Navicat Premium Data Transfer
Source Server : 我的MySQL
Source Server Type : MySQL
Source Server Version : 60011
Source Host : localhost:3306
Source Schema : itcast
Target Server Type : MySQL
Target Server Version : 60011
File Encoding : 65001
Date: 29/08/2023 19:17:22
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for emp
-- ----------------------------
DROP TABLE IF EXISTS `emp`;
CREATE TABLE `emp` (
`ID` int(11) NULL DEFAULT NULL COMMENT '编号',
`WORKNO` varchar(10) CHARACTER SET gbk COLLATE gbk_chinese_ci NULL DEFAULT NULL COMMENT '工号',
`NAME` varchar(10) CHARACTER SET gbk COLLATE gbk_chinese_ci NULL DEFAULT NULL COMMENT '姓名',
`GENDER` char(1) CHARACTER SET gbk COLLATE gbk_chinese_ci NULL DEFAULT NULL COMMENT '性别',
`AGE` tinyint(3) UNSIGNED NULL DEFAULT NULL COMMENT '年龄',
`IDCARD` char(18) CHARACTER SET gbk COLLATE gbk_chinese_ci NULL DEFAULT NULL COMMENT '身份证号',
`WORKADDRESS` varchar(50) CHARACTER SET gbk COLLATE gbk_chinese_ci NULL DEFAULT NULL COMMENT '工作地址',
`ENTRYDATE` date NULL DEFAULT NULL COMMENT '入职时间'
) ENGINE = InnoDB CHARACTER SET = gbk COLLATE = gbk_chinese_ci COMMENT = '员工表' ROW_FORMAT = Compact;
-- ----------------------------
-- Records of emp
-- ----------------------------
INSERT INTO `emp` VALUES (1, '1', '柳岩', '女', 20, '123456789012345678', '北京', '2000-01-01');
INSERT INTO `emp` VALUES (2, '2', '张无忌', '男', 18, '123456789012345670', '北京', '2005-09-01');
INSERT INTO `emp` VALUES (3, '3', '韦一笑', '男', 38, '123456789712345670', '上海', '2005-08-01');
INSERT INTO `emp` VALUES (4, '4', '赵敏', '女', 18, '123456757123845670', '北京', '2009-12-01');
INSERT INTO `emp` VALUES (5, '5', '小昭', '女', 16, '123456769012345678', '上海', '2007-07-01');
INSERT INTO `emp` VALUES (6, '6', '杨道', '男', 28, '12345678931234567X', '北京', '2006-01-01');
INSERT INTO `emp` VALUES (7, '7', '范骚', '男', 40, '123456789212345670', '北京', '2005-05-01');
INSERT INTO `emp` VALUES (8, '8', '黛绮丝', '女', 38, '123456157123645670', '天津', '2015-05-01');
INSERT INTO `emp` VALUES (9, '9', '范凉凉', '女', 45, '123156789012345678', '北京', '2010-04-01');
INSERT INTO `emp` VALUES (10, '10', '陈友凉', '男', 53, '123456789012345670', '上海', '2011-01-01');
INSERT INTO `emp` VALUES (11, '11', '张士诚', '男', 55, '123567897123465670', '江苏', '2015-05-01');
INSERT INTO `emp` VALUES (12, '12', '常遇春', '男', 32, '123446757152345670', '北京', '2004-02-01');
INSERT INTO `emp` VALUES (13, '13', '张三丰', '男', 88, '123656789012345678', '江苏', '2020-11-01');
INSERT INTO `emp` VALUES (14, '14', '灭绝', '女', 65, '123456719012345670', '西安', '2019-05-01');
INSERT INTO `emp` VALUES (15, '15', '胡青牛', '男', 70, '12345674971234567X', '西安', '2018-04-01');
INSERT INTO `emp` VALUES (16, '16', '周芷若', '女', 18, NULL, '北京', '2012-06-01');
SET FOREIGN_KEY_CHECKS = 1;
(1).DQL语法
1.SQL的Select 执行顺序如下:
(1) FROM
(3) JOIN
(2) ON
(4) WHERE
(5)GROUP BY (开始使用SELECT中的别名,后面的语句中都可以使用) - 也就是非聚合函数可以使用了
(6) AVG,SUM....
(7)HAVING
(8) SELECT
(9) DISTINCT
(10) ORDER BY
(11)LIMIT
2.常见的SQL语法
select
字段列表
from
表名列表
where
条件列表
group by
分组字段列表
having
分组后排序列表
order by
排序字段列表
limit
分页参数
(2).DQL_基本查询
1.查询多个字段
select 字段1,字段2.. from 表名;
2.查询全部字段
select *from 表名;
3.设置别名
select 字段1 [as 别名1],字段2[as 别名2] ... from 表名;
4.去除重复记录
select distinct 字段列表 from 表名;
(3).DQL_条件查询
1.基本语法
select 字段列表 from 表名 where 条件列表;
2.条件
- 比较运算符
> >= 大于
< <= 小于
= 等于
<> != 不等于
between ... and ... 在某个范围之内包含本身
in(...) 在in之后的列表中的值,任选一个即可
like '占位符' 模糊匹配( 占位符是 _ 匹配单个字符, 占位符是 % 匹配任意个字符)
is null 值为空
is not null 值不为空
- 逻辑运算符
and && 且条件
or || 或条件
not ! 非,不是
(4).聚合函数
聚合函数: 将一列数据作为一个整体,进行纵向计算。
1.常见的聚合函数
count() #统计数量->统计的是条数不是求和
max() #最大值
min() #最小值
avg() #平均值
sum() #求和
2.聚合函数语法
select 聚合函数(字段列表) from 表名;
注意:
- null值不参与聚合函数运算。
(5).DQL_分组查询
1.语法
select 字段列表 from 表名 [where 条件] group by 分组字段名 [having 分组后过滤条件];
2.where与having的区别
- 执行时机 : where是分组之前进行过滤,不满足where条件,不参与分组;而having是分组之后对结果进行过滤。 也就是说where的优先级高
- 判断条件不同 : where不能对聚合函数进行判断,而having可以。 也就是说having的优先级比较低
- 示列
3.1根据性别分组,同级男性员工 和 女性员工的数量
SELECT gender,COUNT(*) FROM emp GROUP BY gender;
3.2根据性别分组,同级男性员工 和 女性员工的平均年龄
SELECT gender,avg(age) FROM emp GROUP BY gender;
3.3查询年龄小于45的员工,并根据工作地址分组,获取员工数量大于等于3的工作地址
SELECT WORKADDRESS,COUNT(*) FROM emp WHERE age<45 GROUP BY WORKADDRESS HAVING COUNT(*)>=3;
注意:
- 进行分组查询的时候,返回的字段通常是分组的条件,返回其他的没有意义。并不是说会报错只是没有意义。
(6).DQL_排序查询
1.支持多条件排序
先按照第一种字段进行排序,假如顺序相同那么就按照第二种字段进行排序。
select 字段列表 from 表名 order by 字段1 排序方式1, 字段2 排序方式2; #先按照第一种字段进行排序,假如顺序相同那么就按照第二种字段进行排序
2.排序方式
- ASC: 升序(默认值)
- DESC: 降序
- 示列
3.1根据年龄对公司员工进行升序排序
SELECT * FROM emp ORDER BY age ASC;
3.2 根据入职时间对员工进行降序排序
SELECT * FROM emp ORDER BY ENTRYDATE DESC;
3.3 根据年龄对公司的员工进行升序排序,年龄相同,再按照入职时间进行降序排序
SELECT * FROM emp ORDER BY age ASC,ENTRYDATE DESC;
(7).DQL_分页查询
1.分页语法
select 字段列表 from 表名 limit 起始索引,查询记录数;
注意:
- 起始索引从0开始, 起始索引=(查询页码-1)*每页显示数
- 分页查询是数据库的方言,不同的数据库有不同的实现,MySQL中是Limit
- 如果查询的是第一页数据,起始索引可以省略,直接简写为 limit 10。
- 示列
2.1 查询第一页员工数据,每页展示10条
SELECT * FROM emp LIMIT 10
2.2 查询第二页的十条数据
SELECT * FROM emp LIMIT 10,10
(8).DQL_示列
1.查询年龄为20,21,22,23岁的女性员工信息。
SELECT * FROM emp WHERE gender = '女' AND age in(20,21,22,23);
2.查询性别为男,并且年龄在 20-40 岁以内的姓名为三个字的员工。
SELECT * FROM emp WHERE gender = '男' AND name like'___' AND age BETWEEN 20 AND 40
3.统计员工表中,年龄小于60岁的,男生员工和女性员工的人数
SELECT gender,count(*) FROM emp WHERE age<60 GROUP BY gender
4.查询所有年龄小于等于35岁员工的姓名和年龄,并对查询结果按年龄升序排序,如果年龄相同按照入职降序排序
SELECT name,gender,age FROM emp WHERE age<=35 ORDER BY age ASC,entrydate DESC
5.查询性别为男,且年龄在20-40岁以内的前5个员工,对查询的结果按年龄升序排序,年龄相同按入职时间升序排序
SELECT * FROM emp WHERE gender='男' AND age BETWEEN 10 and 70 ORDER BY age asc, entrydate desc LIMIT 5
6. DCL (数据控制语言)
(1).DCL 介绍
DCL英文全称是 Data Controller Language (数据控制语言),用来管理数据库用户、控制数据库的访问权限。简单的说就是不同的用户具有不同的管理权限。
(2).DCL 管理用户
- 查询用户
use mysql;
select * from user;
- 创建用户
create use '用户名'@'主机名' identified by '密码'
- 修改用户
alter user '用户名'@'主机名' identified with mysql_native_password by '新密码';
- 删除用户
drop user '用户名'@'主机名';
(3).DCL_权限控制
MySQL 中定义了很多中权限,但是常用的就以下几种。
1.查询权限
show grants for '用户名'@'主机名';
2.授予权限
grant 权限列表 on 数据库名.表名 to '用户名'@'主机名';
3.撤销权限
revoke 权限列表 on 数据库名 from '用户名'@'主机名'
(三)、函数 - (非聚合函数)
函数: 是指一段可以直接被另一段程序调用的程序或代码。
1. 字符串函数
(1).基础知识
concat(s1,s2...sn)
,将s1 s2 字符串拼接成新的字符串。- 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。
(2).字符串语法
select 函数;
(3).示列
1. contact
SELECT concat('hello','mysql');
2. lower 和 upper
select lower('Hello')
select upper('Hello')
3. lpad 和 rpad
select LPAD('李明',10,'_')
4. substring
select SUBSTRING('abcd',1,2)
5.由于业务需求变更,企业员工的工号,统一为5位数,目前不足5位数的全部在前面补0。比如: 1号员工的工号应该为00001
update emp set WORKNO = LPAD(WORKNO,6,'0')
2.数值函数
(1).基础知识
常见的数值函数如下:
- ceil(x), 向上取整。
- floor(x),向下取整。
- mod(x,y),返回x/y的模,也就是余数。
- rand(),返回0~1内的随机数。
- round(x,y),求参数x的四社五入的值,保留y位小数。
(2).示列
通过数据库函数,生成一个随机六位数的密码。
SELECT LPAD(substring(RAND()*1000000,1,6),6,'0')
3.日期函数
(1).基本知识
- curdate(),返回当前日期。
- curtime(),返回当前时间。
now()
,返回当前日期和时间。year(date)
,获取指定date的年份。month(date)
,获取指定date的月份。day(date)
,获取指定date的日期。date_add(date,interval expr type)
。返回一个日期/时间值间隔expr后的值。datediff(date1(减数),date2(被减数))
,返回起始时间date1和结束时间date之间的天数。
(2).示列
1.返回当前时间
now()
2.返回指定间隔后时间是多少
select date_add(now(), interval 70 day)
3.查询入职时间为多少天
select datediff( '2023-11-08 18:45:17',now())
4.查询所有员工的入职天数,并根据入职天数倒叙排序
SELECT `NAME`,DATEDIFF(NOW(),ENTRYDATE) FROM emp ORDER BY DATEDIFF(NOW(),ENTRYDATE) DESC
4.流程函数
流程函数也是很常用的一类函数,可以在SQL语句中实现条件筛选,从而提高语句的效率。
(1).基本知识
1.if(value,t,f)
,如果value为true,则返回t,否则返回f。
2. ifnull(value1,value2)
,如果value1不为空,返回value1,否则返回value2。
3. case when [val1] then [res1] ... else [default] end
, 如果val1为true,返回res1, …否则返回default默认值。
4. case [expr] when [val1] then [res1] ... else [default] en
,如果expr的值等于val1,返回res1,否则返回default默认值。
(2).示列
1.查询emp表的员工姓名和工作地址(北京/上海 --->展示为一线城市,其他的为二线城市)
使用4
SELECT `NAME`,WORKADDRESS,( CASE WORKADDRESS ⭐
WHEN '北京' THEN
'一线城市'
WHEN '上海' THEN
'一线城市'
ELSE
'二线城市'
END )
FROM emp;
2.统计班级各个学员的成绩,展示的规则如下: >=85优秀,>=60及格,否则不及格。
使用3
SELECT `NAME`,AGE,CASE ⭐
WHEN AGE>=70 THEN
'优秀'
WHEN AGE>=60 AND AGE<70 THEN
'及格'
ELSE
'不及格'
END FROM emp;
# 也可以设置多条流程函数用于求单科的优秀还是不优秀。⭐
SELECT `NAME`,AGE,
(CASE WHEN AGE>=70 THEN '优秀' WHEN AGE>=60 AND AGE<70 THEN '及格' ELSE '不及格'END),
(CASE WHEN AGE>=70 THEN '优秀' WHEN AGE>=60 AND AGE<70 THEN '及格' ELSE '不及格'END)
FROM emp;
注意
- when 和 then 可以写多个代表着 或 的意思。
(四)、约束
1.约束概述
(1).约束的概念
概念: 约束时作用于表中字段上的规则,用于限制存储在表中的数据。
(2).约束的目的
保证数据库中数据的正确、有效性和完整性。
(3).约束的分类
- 非空约束(Not Null) 限制字段的数据不能为null
- 唯一约束(unique) 保证该字段的所有数据都是唯一、不重复
- 主键约束(primary key) 主键是一行数据的唯一标识,要求非空且唯一
- 默认约束(Default) 保存数据时,如未指定字段则采用默认值
- 检查约束(check_8.0.16版本支持) 保证字段值满足某一个条件
- 外键约束(foreign key) 用来让两张表之间建立联系,保证数据的一致性和完整性。
注意: 约束是作用于表中字段上的,可以创建表/修改表的时候添加约束。
2.约束演示
# 会报错,因为的check约束是8.0.16版本以后才支持的,我们的数据库6.0.13版本的,所以会报错
create table user(
id int PRIMARY key auto_increment comment '主键',
name varchar(20) not null unique comment '姓名',
age int check(age>0 && age<=120) comment '年龄', # 年龄在0~120岁之间
statu char(1) default '1' comment '状态',
gender char(1) comment '性别'
);
3.外键约束
(1).外键约束的概念
外键用来让两张表的数据之间建立连接,从而保证数据的一致性和完整性。
(2).SQL语句
创建一个新的数据库: 数据库名字叫做itheima
create table dept(
id int primary key auto_increment,
name varchar(10) );
insert into dept values (null, '研发部'), (null, '市场部'), (null, '财务部'), (null, '销售部'), (null, '总经办'), (null, '人事部');
create table emp( id int primary key auto_increment,
name varchar(10),
age int, job varchar(10),
salary int,
entrydate date,
managerid int,
dept_id int,
constraint fk_dept foreign key (dept_id) references dept(id)
); ⭐
insert into emp values (null, '金庸', 66, '总裁', 20000, '2000-01-01', null, 5), (null, '张无忌', 20, '项目经理', 12500, '2005-12-05', 1, 1), (null, '杨逍', 33, '开发', 8400, '2000-11-03', 2, 1), (null, '韦一笑', 48, '开发', 11000, '2002-02-05', 2, 1), (null, '常遇春', 43, '开发', 10500, '2004-09-07', 3, 1), (null, '小昭', 19, '程序员鼓励师', 6600, '2004-10-12', 2, 1), (null, '灭绝', 60, '财务总监', 8500, '2002-09-12', 1, 3), (null, '周芷若', 19, '会计', 4800, '2006-06-02', 7, 3), (null, '丁敏君', 23, '出纳', 5250, '2009-05-13', 7, 3), (null, '赵敏', 20, '市场部总监', 12500, '2004-10-12', 1, 2), (null, '鹿杖客', 56, '职员', 3750, '2006-10-03', 10, 2), (null, '鹤笔翁', 19, '职员', 3750, '2007-05-09', 10, 2), (null, '方东白', 19, '职员', 5500, '2009-02-12', 10, 2), (null, '张三丰', 88, '销售总监', 14000, '2004-10-12', 1, 4), (null, '俞莲舟', 38, '销售', 4600, '2004-10-12', 14, 4), (null, '宋远桥', 40, '销售', 4600, '2004-10-12', 14, 4), (null, '陈友谅', 42, null, 2000, '2011-10-12', 1, null);
create table salgrade( grade int, losal int, hisal int );
insert into salgrade values (1, 0, 3000), (2, 3001, 5000), (3, 5001, 8000), (4, 8001, 10000), (5, 10001, 15000), (6, 15001, 20000), (7, 20001, 25000), (8, 25001, 30000);
(3).外键约束的语法
具有外键语法的表称为从表、不具有外键语法的表成为主表。
- 语法
第一种添加外键- 创建表的时候
create table 表名(
字段名 数据类型,
...
[constraint] [外键名称] foreign key(外键字段名) references 主表(主表列名)
)
第二种添加外键- 创建表之后
alter table 从表表名 add constraint 外键名称 foreign key(外键字段名) references 主表(主表列名)
删除外键
alter table 从表表名 drop foreign key 外键名称。
(4).有外键约束与外键约束情况
- 无外键的情况下
假如两张表数据是相互联系作用的,在没有绑定主外键的情况下,一张表数据的删除将不会影响到另一张表的删除。从而出现了数据不同步的情况。
- 有外键的情况下
我们尝试删除主表中的数据,我们发现提示我们删除不了这个字段,因为在从表中有行使用我们这个字段。
4.外键删除更新行为
(1).删除/更新行为
- not action : 在主表中删除/更新对应记录时,首先
检查该记录是否对应外键
,如果有则不允许删除/更新。 - restrict : 当在主表中删除/更新记录时,首先
检查该记录是否有对应外键
,如果有则不允许删除/更新。 - cascade(级联) : 当在主表中删除/更新对应记录时,首先
检查记录是否对应外键,如果有,则也删除/更新外键在子表中的记录
。 - set null : 当在主表中删除对应记录时,首先
检查该记录是否有对应外键,如果有则设置子表中该外键值为null
(这就要求前提是外键允许null) - set deffault 主表有变更时,子表将外键列设置成一个默认的值(
Innodb不支持
)
注意: MySQL默认支持 no action 和 restrict 这两种行为机制。
(2).修改删除/更新行为语法
alter table 从表表名 add constraint 外键名称 foreign key (外键字段) references 主表名(主表字段名) on update xxxx on delete xxxx;
(3).级联删除/更新测试
# 先删除外键
alter table emp drop foreign key fk_dept;
# 再添加外键
alter table emp add constraint fk_dept foreign key (dept_id) references dept(id) on update CASCADE on delete cascade;
- 我们将主表的编码为2 更改为6.根据级联的特性,所以从表的字段内容也随着改变。
(4). set null 删除/更新测试
# 先删除外键
alter table emp drop foreign key fk_dept;
# 再添加外键
alter table emp add constraint fk_dept foreign key (dept_id) references dept(id) on update set NULL on delete set null;
1.我们将我们的主键设置成6更改成8,发现从表数据修改为null
(五)、多表查询
1.多表关系
(1).多表查询概述
项目开发中,再进行数据表结构设计时,会根据业务需求及业务模块之间的关系,分析并设计表结构,由于业务之间相互关联,所以各个表结构之间也存在着各种联系,基本上分为:
- 一对一
- 多对多
- 一对多(多对一)
(2).一对多 (多对一)关系
这种关系最典型的列子就是 员工和部门的关系。员工为N 部门为1。
这种我们通常在从表(员工表)也就是员工表创建一个外键,与主表(部门表)进行联系。
(3).多对多关系
这种关系最典型的列子就是 学生与课程的关系。一个学生可以选择多门课程,一门课程可以供多各学生选择。
这种我们需要建立第三张表(中间表)并包括两个外键,第一个外键需要与学生表进行关联,另一张表需要和课程表进行关联。
建立 学生表+课程表+中间表
create table student(
id int auto_increment primary key comment '主键ID',
name varchar(10) comment '姓名',
no varchar(10) comment '学号'
) comment '学生表' charset=utf8;
insert into student values (null, '黛绮丝', '2000100101'),(null, '谢逊','2000100102'),(null, '殷天正', '2000100103'),(null, '韦一笑', '2000100104') ;
create table course(
id int auto_increment primary key comment '主键ID',
name varchar(10) comment '课程名称'
) comment '课程表' charset=utf8;
insert into course values (null, 'Java'), (null, 'PHP'), (null , 'MySQL') ,(null, 'Hadoop');
create table student_course(
id int auto_increment comment '主键' primary key,
studentid int not null comment '学生ID',
courseid int not null comment '课程ID',
constraint fk_courseid foreign key (courseid) references course (id),
constraint fk_studentid foreign key (studentid) references student (id)
)comment '学生课程中间表' charset=utf8;
insert into student_course values (null,1,1),(null,1,2),(null,1,3),(null,2,2),(null,2,3);
(4)一对一关系
这种关系最典型的列子就是 用户与用户详情的关系。
一对一关系,多用于单表拆分,将一张表的基础字段放在一张表中,其他详情字段放在另一张表中,以提升操作效率。
这种关系我们需要在 任意一方假如外键,关联另外一方的主键,并且设置外键为唯一的(unique)
create table tb_user(
id int auto_increment primary key comment '主键ID',
name varchar(10) comment '姓名',
age int comment '年龄',
gender char(1) comment '1: 男 , 2: 女',
phone char(11) comment '手机号'
) comment '用户基本信息表';
create table tb_user_edu(
id int auto_increment primary key comment '主键ID',
degree varchar(20) comment '学历',
major varchar(50) comment '专业',
primaryschool varchar(50) comment '小学',
middleschool varchar(50) comment '中学',
university varchar(50) comment '大学',
userid int unique comment '用户ID',
constraint fk_userid foreign key (userid) references tb_user(id)
) comment '用户教育信息表';
insert into tb_user(id, name, age, gender, phone) values
(null,'黄渤',45,'1','18800001111'),
(null,'冰冰',35,'2','18800002222'),
(null,'码云',55,'1','18800008888'),
(null,'李彦宏',50,'1','18800009999');
insert into tb_user_edu(id, degree, major, primaryschool, middleschool,
university, userid) values
(null,'本科','舞蹈','静安区第一小学','静安区第一中学','北京舞蹈学院',1),
(null,'硕士','表演','朝阳区第一小学','朝阳区第一中学','北京电影学院',2),
(null,'本科','英语','杭州市第一小学','杭州市第一中学','杭州师范大学',3),
(null,'本科','应用数学','阳泉第一小学','阳泉区第一中学','清华大学',4);
2.多表查询概述
(1).笛卡尔积效应
笛卡尔积时指在数学中,两个集合A集合和B集合的所有组合情况(排列组合)。(在多表查询时,需要消除无效的笛卡尔积
)
-- 多表查询,会发现出现笛卡尔积效应
select *from emp,dept;
(2).消除笛卡尔积效应
消除笛卡尔积效应的实质就是 消除掉不符合条件的排列组合数据。
-- 消除无效的笛卡尔积
select *from emp,dept where emp.dept_id=dept.id;
3.多表查询分类
(1).连接查询
- 内连接: 相当于查询
A、B交集
部分数据 - 外连接:
- 左外连接: 查询
左表所有数据
,以及两张表交集部分数据 - 右外连接:查询
右表所有数据
,以及两张表交集部分数据
- 左外连接: 查询
- 自连接: 当前表与自身的连接查询,自连接必须使用表别名
(2).子查询
概念:SQL语句中嵌套Select语句,称为嵌套查询,又称子查询。
select *from 表1 where column1=(select column1 from 表2)
子查询外部的语句可以是 insert/update/delete/select的任何一个。
4.连接查询-内连接 (两张表交集部分) ⭐
(1).隐式内连接语法
selec 字段列表 from 表1,表2 where 条件...
(2).显示内连接语法
select 字段列表 from 表1 [inner] join 表2 on 连接条件...;
(3).隐式内连接和显示内联接测试
- 隐式内连接 查询部门员工和对应部门
-- 隐式内连接使用where进行判断
select emp.`name`,dept.name from emp,dept on emp.dept_id=dept.id;
-- 隐式内连接起别名,起玩别名之后只能用别名操作了
select ep.`name`,dt.name from emp as ep,dept as dt on ep.dept_id=dt.id;
- 显示内连接查询部门员工
-- 显示内连接
select emp.`name`,dept.name from emp inner join dept where emp.dept_id=dept.id;
-- 显示内连接 可省略inner
select emp.`name`,dept.name from emp inner join dept where emp.dept_id=dept.id;
5.连接查询-外连接 (查询某张表的全部数据和交集部分)
相当于查询 left 或 right 左侧的第一个表的全部数据。
(1).外连接查询语法
- 左外连接
这里相当于查询表1 的所有数据和表一与表二的交集
select 字段列表 from 表1 left [outer] join 表2 on 条件;
- 右外连接
这里相当于查询表2 的所有数据和表一与表二的交集
select 字段列表 from 表1 right [outer] join 表2 on 条件;
(2).左外连接测试
关键字 left
-- 左外连接演示
-- 1.查询emp表的所有数据,和对应的部门信息(左外连接)
select * from emp left join dept on emp.dept_id=dept.id;
-- 2.查询emp表的所有数据,和对应的部门信息(左外连接) 起别名
select *from emp as ep left join dept as dt on ep.dept_id=dt.id;
(3).右外连接测试
关键字 right
-- 1.查询dept表的所有数据,和对应的员工信息
select dept.*,emp.`name` from emp right join dept on emp.dept_id=dept.id;
6.连接查询-自连接 (可交集、可单独) ⭐
子连接查询语法,一定要给表起别名。
(1).自连接查询语法
select 字段列表 from 表A 别名A join 表A 别名B on 条件...;
自连接查询,可以是内连接查询,也可以是外连接查询。
(2).自连接测试
- 查询员工 及其 所属领导的名字
不管是什么连接都是经历了 排列组合 实现的数据查询
-- 自连接
-- 1.查询员工 及其 所属领导的名字, 这里的ep_1相当于员工表,ep_2相当于领导表。 利用员工表的领导id 找 员工表的id
select ep_1.`name`,ep_2.`name` from emp ep_1 join emp ep_2 on ep_1.managerid=ep_2.id;
- 查询所有员工 emp 及其 领导的名字emp,如果员工没有领导,也需要查询出来。
-- 查询所有员工 emp 及其 领导的名字emp,如果员工没有领导,也需要查询出来
select ep_1.`name`,ep_2.`name` from emp ep_1 left join emp ep_2 on ep_1.managerid=ep_2.id;
7.联合查询-union,union all
对于union查询,就是把多次查询的结果合并起来,形成一个新的查询结果集。
(1).联合查询的语法
select 字段列表 from 表A...
union [all]
select 字段列表 from 表B...
(2).联合查询示列
- 查询出所有满足的->不去重
-- 将薪资低于5000的员工,和年龄大于50岁的员工全部查询出来
select *from emp WHERE salary<5000
union all #⭐
select *from emp WHERE age>50;
- 查询出所有满足的->去重
-- 将薪资低于5000的员工,和年龄大于50岁的员工全部查询出来 (去重)
select *from emp WHERE salary<5000
union # ⭐
select *from emp WHERE age>50;
注意:
- 对于联合查询的多张表的列数必须保持一致,字段类型也需要保持一致。
- union all会将全部的数据直接合并在一起,union会对合并之后的数据进行去重的操作。
8.子查询 (嵌套查询) ⭐
(1).子查询的基本语法
概念:SQL语句中嵌套Select语句,称为嵌套查询,又称子查询。
select *from 表1 where column1=(select column1 from 表2)
子查询外部的语句可以是 insert/update/delete/select的任何一个。
(2).子查询的分类
根据子查询结果不同,分为:
- 标量子查询(子查询结果为单个值)
- 列子查询(子查询结果为一列)
- 行字查询(子查询结果为一行)
- 表子查询(子查询结果为多行多列)
根据子查询位置,分为:where之后、from之后、select之后。
9.子查询-标量子查询 (返回结果是一个值)
(1).标量子查询
子查询返回的结果是单个值(数字、字符串、日期等)最简单的形式,这种子查询成为标量子查询。
常用的操作符: <> = > >= < <=
(2).标量子查询示列
1..查询销售部的所有员工
-- 标量子查询
-- 1.查询销售部的所有员工信息
-- 1.1先查找销售部的id
select id from dept where `name`='销售部'
-- 1.2再查找这个数据
select *from emp where emp.dept_id=4;
-- 完整写法
select *from emp where emp.dept_id= (select id from dept where `name`='销售部');
2.查询在房东白入职之后的员工信息
-- 查询在方东白入职之后的员工信息
select entrydate from emp where name ='方东白';
select *from emp where entrydate>'2009-02-12';
-- 完整写法
select *from emp where entrydate>(select entrydate from emp where name ='方东白');
10.子查询-列子查询 (单列但可多行)
返回结构是一列数据 比如: 所有查询所有员工的id。 也就是一行数据里面的一个字段,但是可以是多行的同一字段。
(1).列子查询
子查询返回的结果是一列(可以是多行),这种子查询称为列子查询。
常用的操作符: IN、NOT IN、ANY、SOME、ALL。
- IN : 在指定的集合范围之内,多选一。
- NOT IN: 不在指定的集合范围之内。
- ANY: 子查询返回列表,有任意一个满足即可。
- SOME: 与ANY等同,使用SOME的地方都可以使用ANY。
- ALL:子查询返回列表的所有值都必须满足。
(2).列子查询示列
1.查询销售部和市场部的所有员工
-- 列子查询
-- 1.查询销售部和市场部的所有员工
select id from dept where dept.`name` in ('市场部','销售部');
select *from emp where emp.dept_id in (2,4);
select *from emp where emp.dept_id in (select id from dept where dept.`name` in ('市场部','销售部'));
2.查询比财务部所有人工资都高的员工信息
-- 2.查询比财务部所有人工资都高的员工
第一种:使用聚合函数
-- 2.1 首先查询财务部的部门id
select id from dept where dept.`name`='财务部'
-- 2.2然后聚合函数查询财务部的最高工资
select max(emp.salary) from emp where emp.dept_id=(select id from dept where dept.`name`='财务部')
-- 2.3 查询信息
select *from emp where emp.salary >= (select max(emp.salary) from emp where emp.dept_id=(select id from dept where dept.`name`='财务部'));
第二种:使用列子查询
select *from emp where emp.salary >= all(select emp.salary from emp where emp.dept_id=(select id from dept where dept.`name`='财务部'));
3.查询比开发部任何一人工资低的员工信息
-- 3. 查询比研发部其中任意一人工资高的员工信息
第一种: 使用聚合函数
select dept.id from dept where dept.`name`='研发部';
select min(emp.salary) from emp where emp.dept_id = (select dept.id from dept where dept.`name`='研发部');
select *from emp where emp.salary >=(select min(emp.salary) from emp where emp.dept_id = (select dept.id from dept where dept.`name`='研发部'));
第二种:使用列子查询 (some、any)
select *from emp where emp.salary >= some(select emp.salary from emp where emp.dept_id = (select dept.id from dept where dept.`name`='研发部'));
11.子查询-行子查询 (单行但可多列)
返回的结果是一行,比如说查询 和 张无忌的薪资结构相同且领导相同的员工信息。薪资结构和领导都是张无忌一行的数据,一行数据多列字段。
(1).行子查询
子查询返回的结果是一行(可以是多列),这种子查询称为行子查询。
(2).行子查询示列
1.查询与 张无忌的薪资结构 及 直属领导 相同的员工信息。查询的是张无忌一个
-- 行子查询
-- 1.查询与 张无忌的薪资结构 及 直属领导 相同的员工信息
select salary '薪资结构',managerid '管理者' from emp where emp.`name`='张无忌';
select *from emp where (emp.salary,emp.managerid) =(12500,1);
⭐
select *from emp where (emp.salary,emp.managerid) =(select salary '薪资结构',managerid '管理者' from emp where emp.`name`='张无忌');
12.子查询-表子查询 (多行且多列)
比如: 两行分别是 鹿杖客 宋远桥 这两行,两列 分别是 薪资结构和职位。
(1).表子查询
子查询返回的结果是 多行多列
,这种子查询称为表子查询。
(2).表子查询示列
1.1.查询与 鹿杖客 宋远桥 的职位和薪资相同的员工信息
-- 表子查询
-- 1.查询与 鹿杖客 宋远桥 的职位和薪资相同的员工信息
select emp.job,emp.salary from emp where emp.`name` in ('鹿杖客','宋远桥');
-- 这里放in 表示要么一起满足这一行数据,要么一起满足下一行数据
select *from emp where (emp.job,emp.salary) in (select emp.job,emp.salary from emp where emp.`name` in ('鹿杖客','宋远桥'));
2.查询入职日期是 2006-01-01 之后的员工信息,及其部门信息
-- 2. 查询入职日期是 2006-01-01 之后的员工信息,及其部门信息
# 这个方法查询不完整,只查询到了交集,而要求我们查询一张表的全部信息
select *from emp,dept where emp.entrydate > '2006-01-01' and emp.dept_id=dept.id;
select *from (select * from emp where emp.entrydate> '2006-01-01') as emp_a left join dept on emp_a.dept_id=dept.id;
注意: 我们新派生一张表的时候,我们要给这个新派生的表起别名,否则会报错。
起别名之后:
-- 1.查询员工的姓名,年龄、职位、部门信息 (隐式内连接)
select emp.`name`,emp.age,emp.job,dept.id from emp,dept where emp.dept_id = dept.id;
-- 2.查询年龄小于30岁的员工的姓名,年龄,职位,部门信息(显示内连接)
#这里一定要使用where,如果使用and的话显示内连接会有效,但是外连接就会无效。
select *from emp join dept on emp.dept_id=dept.id where emp.age>30;
-- 3.查询拥有员工的部门ID、部门名称 (交集)
select DISTINCT dept.id,dept.`name` from dept join emp on dept.id=emp.dept_id;
-- 4.查询所有年龄大于40岁的员工,及其归属的部门名称;如果没有分配部门,也需要展示出来 (左外连接)
#这里千万不要使用and,如果使用and,外连接将会失效
select emp.`name`,dept.`name` from emp left join dept on emp.dept_id = dept.id where emp.age>40;
-- 5.查询所有员工的工资等级 : 员工表和薪资等级表是没有外键关联(直接在笛卡尔积中赛选了) (隐式内连接)
select emp.`name`,emp.salary,salgrade.grade from emp,salgrade where emp.salary>=salgrade.losal and emp.salary<=salgrade.hisal;
-- 6.查询 研发部 所有员工的信息及工资等级 (隐式内连接)
select emp.`name`,emp.salary,dept.`name`,salgrade.grade from emp,salgrade join dept on dept.id=(select id from dept where dept.name='研发部') where emp.salary>=salgrade.losal and emp.salary<=salgrade.hisal;
-- 7.查询研发部 员工的平均薪资 (隐式内连接)
select avg(emp.salary) from emp join dept on dept.id=emp.dept_id where dept.id=(select id from dept where dept.name='研发部');
-- 8. 查询工资比 灭绝 高的员工信息 (联表)
select * from emp where emp.salary >(select salary from emp where emp.`name`='灭绝');
-- 9.查询比平均薪资高的员工信息 (联表)
select *from emp where emp.salary>(select avg(emp.salary) from emp);
-- 10.查询低于本部门平均工资的员工信息 (自连接)
select * from emp e2 where e2.salary<(select avg(e1.salary) from emp e1 where e1.dept_id =e2.dept_id);
-- 11.查询所有的部门信息,并统计部门的员工人数 (分组)
select count(*),dept.`name` from emp,dept where dept.id=emp.dept_id GROUP BY emp.dept_id;
-- 12.查询所有学生的选课情况,展示出学生名称、学号、课程名称。
select *from student,course where (student.id,course.id) in (select student_course.studentid,student_course.courseid from student_course)
注意:
- on后面的是联查条件,where是非联查条件。非联查条件不能用on;联查条件不能用where。
(六)、事务
1.事务简介
事务 是一组操作的集合,他是一个不可分割的工作单位,事务会把所有的操作作为一个整体一起向系统提交或撤销操作请求,即浙西操作要么同时成功,要么同时失败。
drop table if exists account;
create table account(
id int primary key AUTO_INCREMENT comment 'ID',
name varchar(10) comment '姓名',
money double(10,2) comment '余额'
) comment '账户表';
insert into account(name, money) VALUES ('张三',2000), ('李四',2000);
注意:
默认MySQL的事务是自动提交的,也就是说,当执行完一条DML语句时,MySQL会立即隐式的提交事务。
2.事务操作
(1).未出现异常下的事务
张三转账给李四
-- 事务 (张三给李四转账1000)
-- 1.查询张三账户余额
select *from account where name='张三';
-- 2.将张三账户余额-1000
update account set account.money=account.money-1000 where account.`name`='张三';
-- 3.将李四的余额+1000
update account set account.money=account.money+1000 where account.`name`='李四';
(2).出现异常下的事务
当张三转完账单之后,李四账户还没来得及收时,有一个错误
-- 事务 (张三给李四转账1000)
-- 1.查询张三账户余额
select *from account where name='张三';
-- 2.将张三账户余额-1000
update account set account.money=account.money-1000 where account.`name`='张三';
-- 3.将李四的余额+1000
圣诞树上的 -- 制造错误
update account set account.money=account.money+1000 where account.`name`='李四';
3.事务控制 - (第一种方法)
(1).事务管理方法
1.查看/设置事务提交方式
select @@autocommit; #如果为1就是自动提交,如果为0就是不自动提交
set @@autocommit=0; #设置事务不自动提交。
2.设置完手动提交后,我们要进行手动提交
commit; #事务提交
3.假如提交后出现了异常,我们可以执行这个语句进行回滚事务。
rollback; #事务回滚
(2).事务提交示列
- 事务控制 - 手动提交(但是未提交)
-- 事务 (张三给李四转账1000)
select @@autocommit;
set @@autocommit=0;
-- 1.查询张三账户余额
select *from account where name='张三';
-- 2.将张三账户余额-1000
update account set account.money=account.money-1000 where account.`name`='张三';
-- 3.将李四的余额+1000
update account set account.money=account.money+1000 where account.`name`='李四';
- 事务控制 - 手动提交(进行提交)
单独 执行commit之后,以前编写的数据才会开始同步。
commit;
(3).事务回滚示列
- 执行有异常代码未回滚但已提交
-- 事务 (张三给李四转账1000)
select @@autocommit;
set @@autocommit=0;
-- 1.查询张三账户余额
select *from account where name='张三';
-- 2.将张三账户余额-1000
update account set account.money=account.money-1000 where account.`name`='张三';
-- 3.将李四的余额+1000
项目出现异常
update account set account.money=account.money+1000 where account.`name`='李四';
commit;
- 执行有异常代码回滚且已提交
rollback;
在提交之后执行回滚的话,数据并不会回滚。假如进行回滚的话,那么就不要执行提交了;如果执行提交的话,就不用执行回滚了。
rollback; #先回滚后提交
4.事务控制 -(第二种方法)
第二种方式我们不需要设置成手动提交。
(1).事务方法
1.开启事务
start transaction 或 begin;
2. 提交事务
commit;
3.回滚事务
rollback;
(2).事务控制
-- 2. 方法
start transaction
-- 1.查询张三账户余额
select *from account where name='张三';
-- 2.将张三账户余额-1000
update account set account.money=account.money-1000 where account.`name`='张三';
-- 3.将李四的余额+1000
项目出现异常
update account set account.money=account.money+1000 where account.`name`='李四';
rollback; #假如有错我们就进行回滚的操作 catch
commit; #假如运行没有异常进行提交的操作
5.事务四大特性 (ACID)
(1).四大特性
- 原子性: 事务是不可分割的最小操作单元,要么全部成功,要么全部失败。
- 一致性:事务完成时,必须使用所有的数据都保持一致状态。
- 隔离性:数据库系统提供的隔离机制,保证事务在不受外部并发操作影响的独立环境下运行。
- 持久性:事务一旦提交或回滚,他对数据库中的数据的改变就是永久的。
6.并发事务问题
(1).并发事务引起的三大问题
脏读
: 一个事务读到另外一个事务还没有提交的数据。不可重复读
: 一个事务先后读取同一条记录,但两次读取的数据不同,称之为不可重复读。幻读
:一个事务按照条件查询数据时,没有对应的数据行,但是在插入数据时,又发现这行数据已经存在,好像出现了幻影。
(2).三大问题详细介绍
1.脏读:(读取为提交的修改事务,且执行一次查询)
张三开启一个取钱的事务,已经从卡中取了1000,余额还剩1000,但是未关闭服务(也就是还未执行提交)。
这个时候张三老婆同时也开启了一个查询的事务,此时张三老婆查看余额已经读取到了余额1000元。
2.不可重复读: (读取了提交的修改事务,且执行两次查询)
张三老婆正在查询银行卡余额发现有2000元,此时张三开启一个取钱的事务,从卡中取了1000 并提交了事务,
张三老婆再次查找余额,发现余额突然剩下1000了,于是急忙地找工作人员进行处理这个问题。
3.幻读:(读取了提交的新增事务,且执行了一次查询和一次新增)
张三开启事务在办理一个员工的入职手续,由于工作习惯张三 先查询了一下这个员工是否办理过入职,发现没有
便去上个厕所了,但此时另一个同事把这个员工添加了进去并提交了事务,张三上完厕所回来便添加这个员工
发现提示这个员工已经存在,便再次查询了这个员工,发现依然没有。张三
大叫见鬼了!!!!!
7.事务隔离级别
(1).事务隔离级别
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
Read uncommitted | ✅ | ✅ | ✅ |
Read committed | ❌ | ✅ | ✅ |
Repeatable Read(默认) | ❌ | ❌ | ✅ |
Serializable | ❌ | ❌ | ❌ |
(2).查看/操作事务隔离级别
1.查看事务隔离级别
select @@transaction_isolation #版本6.0+
select @@tx_isolation #版本6.0一下
2.设置事务隔离级别
# 假如是session只对当前窗口有效,假如设置的是global那么全部窗口都有效
set [session|global] transaction isolation level {Read uncommitted | Read committed | Repeatable Read | Serializable }
3.版本6.0一下用 select @@transaction_isolation 报错
(3).演示脏读
# global 全局都设置,session 设置当前窗口(会话)
set global transaction isolation level Read uncommitted;
select @@tx_isolation;
结果读取到了未提交的数据