当前位置: 首页 > news >正文

【MySQL】MySQL基础知识详解

文章目录

  • 1. MySQL概述
    • 1.1 数据库相关概念
      • 1.1.1 数据库、数据库管理系统与SQL
      • 1.1.2 关系型数据库与数据模型
    • 1.2 MySQL数据库
      • 1.2.1 MySQL的安装与配置
      • 1.2.2 MySQL服务的启动与停止
      • 1.2.3 连接MySQL服务端
  • 2. SQL
    • 2.1 SQL简介
    • 2.2 DDL
      • 2.2.1 数据库操作
      • 2.2.2 表操作
        • 2.2.2.1 创建表
        • 2.2.2.2 查询表
        • 2.2.2.3 数据类型
        • 2.2.2.4 修改表
        • 2.2.2.5 删除表
    • 2.3 DML
      • 2.3.1 DataGrip的安装与使用
      • 2.3.2 添加数据
      • 2.3.3 修改数据
      • 2.3.4 删除数据
    • 2.4 DQL
      • 2.4.1 基本语法
      • 2.4.2 基础查询
      • 2.4.3 条件查询
      • 2.4.4 聚合函数
      • 2.4.5 分组查询
      • 2.4.6 排序查询
      • 2.4.7 分页查询
      • 2.4.8 执行顺序
      • 2.4.9 综合案例
    • 2.5 DCL
      • 2.5.1 管理用户
      • 2.5.2 权限控制
  • 3. 函数
    • 3.1 字符串函数
    • 3.2 数值函数
    • 3.3 日期函数
    • 3.4 流程函数
  • 4. 约束
    • 4.1 基本约束
    • 4.2 外键约束
      • 4.2.1 介绍
      • 4.2.2 语法
      • 4.2.3 删除/更新行为
  • 5. 多表查询
    • 5.1 多表关系
      • 5.1.1 一对多
      • 5.1.2 多对多
      • 5.1.3 一对一
    • 5.2 多表查询概述
      • 5.2.1 数据准备
      • 5.2.2 概述
      • 5.2.3 分类
    • 5.3 内连接
    • 5.4 外连接
    • 5.5 自连接
      • 5.5.1 自连接查询
      • 5.5.2 联合查询
    • 5.6 子查询
      • 5.6.1 概述
      • 5.6.2 标量子查询
      • 5.6.3 列子查询
      • 5.6.4 行子查询
      • 5.6.5 表子查询
    • 5.7 综合案例
  • 6. 事务
    • 6.1 事务简介
    • 6.2 事务操作
      • 6.2.1 未控制事务
      • 6.2.2 控制事务
    • 6.3 事务四大特性
    • 6.4 并发事务问题
    • 6.5 事务隔离级别

1. MySQL概述

1.1 数据库相关概念

1.1.1 数据库、数据库管理系统与SQL

1.数据库、数据库管理系统与SQL 的概念:

名称简称描述
数据库DataBase(DB)存储数据的仓库,数据是有组织的进行存储
数据库管理系统DataBase Management System (DBMS)操纵和管理数据库的大型软件
SQLStructured Query Language (SQL)操作关系型数据库的编程语言,定义了一套操作

2.数据库、数据库管理系统与SQL之间的关系:通过SQL操作数据库管理系统,再通过数据库管理系统来操作数据库以及数据库当中的数据。

在这里插入图片描述

1.1.2 关系型数据库与数据模型

1.关系型数据库(RDBMS)

概念:建立在关系模型基础上,由多张相互连接的二维表组成的数据库。

而所谓二维表,指的是由行和列组成的表,如下图(就类似于Excel表格数据,有表头、有列、有行,
还可以通过一列关联另外一个表格中的某一列数据)

在这里插入图片描述

特点:

  • 使用表存储数据,格式统一,便于维护。
  • 使用SQL语言操作,标准统一,使用方便。

注:关系型数据库,都是使用SQL语言来进行统一操作,也就是说 MySQLOracleDB2以及 SQLServer等数据库的操作方式都是一致的。

2.数据模型

MySQL是关系型数据库,是基于二维表进行数据存储的,具体的结构图下:

在这里插入图片描述

  • 我们可以通过MySQL客户端连接数据库管理系统DBMS,然后通过DBMS操作数据库。

  • 可以使用SQL语句,通过数据库管理系统操作数据库,以及操作数据库中的表结构及数据。

  • 一个数据库服务器中可以创建多个数据库,一个数据库中也可以包含多张表,而一张表中又可以包含多行记录。

1.2 MySQL数据库

1.2.1 MySQL的安装与配置

1.MySQL的版本

  • 社区版本(MySQL Community Server):免费, MySQL不提供任何技术支持
  • 商业版本(MySQL Enterprise Edition):收费,可以使用30天,官方提供技术支持

2.MySQL的下载

官网下载地址:https://downloads.mysql.com/archives/installer/

在这里插入图片描述
下载完成后,会得到一个安装包 mysql-installer-community-8.0.32.0.msi

3.MySQL的安装

双击安装包进行安装,弹出安装界面后,基本上一直点击Next即可,需要注意的点如下:

  • 如果出现Execute选项,则要先点击Execute,然后再Next
  • 点击Next之后,碰到弹出YesNo的,要点Yes
  • 需要设置Root用户的密码并确认

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

4.配置环境变量

配置环境变量步骤如下:

  • 在此电脑上,右键选择属性,找到 “高级系统设置”,选择环境变量
  • 找到 Path 系统变量, 点击 “编辑”
  • 选择 “新建” , 将MySQL Server的安装目录下的bin目录添加到环境变量 ,C:\Program Files\MySQL\MySQL Server 8.0\bin

在这里插入图片描述

在这里插入图片描述

1.2.2 MySQL服务的启动与停止

MySQL安装完成之后,在系统启动时,会自动启动MySQL服务,我们无需手动启动了。

如果需要启动或者停止MySQL服务,可以使用以下两种方式:

  • 打开终端输入命令,可以启动与停止服务
  • 找到Windows的服务,可以手动启动与停止服务

1.以管理员身份运行cmd,进入命令行执行如下指令,可以启动与停止服务

# 启动服务
net start mysql80
# 停止服务
net stop mysql80

在这里插入图片描述

2.键盘按住Win+R,然后输入services.msc打开服务,找到MySQL80服务,可以右键选择启动或停止服务

在这里插入图片描述
在这里插入图片描述

1.2.3 连接MySQL服务端

客户端连接MySQL服务端有以下两种方式:

  • 使用MySQL提供的客户端命令行工具
  • 使用系统自带的命令行工具执行指令

1.使用MySQL提供的客户端命令行工具

找到最近添加,打开MySQL提供的客户端命令行工具,然后输入Root用户的密码,出现如下图,表示客户端连接成功。

在这里插入图片描述

在这里插入图片描述

注:
1.刚刚安装 MySQL时,设置了Root的用户的密码为123456
2.客户端能成功连接MySQL服务端,表明MySQL数据库安装成功了

2.使用系统自带的命令行工具执行指令

注:使用这种方式,需要配置好环境变量才行

命令格式如下:

mysql [-h 127.0.0.1] [-P 3306] -u root -p
  • -h : MySQL服务所在的主机IP
  • -P : MySQL服务端口号, 默认3306
  • -u : MySQL数据库用户名
  • -p : MySQL数据库用户名对应的密码

打开cmd输入命令,出现如下图,表示客户端连接成功:

在这里插入图片描述

2. SQL

2.1 SQL简介

SQL:结构化查询语言(Structured Query Language),是操作关系型数据库的编程语言,定义了
一套操作关系型数据库统一标准 。

1.SQL的语法

  • SQL语句可以单行或多行书写,以分号结尾
  • SQL语句可以使用空格或缩进来增强语句的可读性
  • MySQL数据库的SQL语句不区分大小写,关键字建议使用大写
  • 注释
    • 单行注释:-- 注释内容# 注释内容(MySQL独有)
    • 多行注释:/* 注释内容 */

2.SQL的分类

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

2.2 DDL

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

2.2.1 数据库操作

数据库操作汇总:

# 查询所有数据库
show databases; # 查询当前数据库
select database();# 创建数据库
create database [ if not exists ] 数据库名 [ default charset 字符集 ] [ collate 排序规则];# 删除数据库
drop database [ if exists ] 数据库名;# 切换数据库
use 数据库名;

1.查询所有数据库

show databases;

在这里插入图片描述

2.切换数据库和查询当前数据库

例:先切换到sys数据库,再查询当前数据库

use sys; 
select database();

在这里插入图片描述

3.创建数据库

例1:创建一个itcast数据库, 使用数据库默认的字符集

create database itcast;

在这里插入图片描述

注:在同一个数据库服务器中,不能创建两个名称相同的数据库,否则将会报错。可以通过 if not exists 参数来解决这个问题,数据库不存在, 则创建该数据库,如果存在,则不创建。

create database if not exists itcast;

在这里插入图片描述

例2:创建一个itheima数据库,并且指定字符集

create database itheima default charset utf8mb4;

在这里插入图片描述

4.删除数据库

例:删除itcast数据库

drop database itcast;

在这里插入图片描述

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

在这里插入图片描述

2.2.2 表操作

表操作:对表的结构进行增删改查

注:此处只涉及对表结构的增删改查,不涉及对表中的数据进行增删改查,对表中的数据进行增删改查的是DML和DQL语句,而此处是DDL语句。

2.2.2.1 创建表

创建表结构:

CREATE TABLE 表名(字段1 字段1类型 [COMMENT 字段1注释 ],字段2 字段2类型 [COMMENT 字段2注释 ],字段3 字段3类型 [COMMENT 字段3注释 ],......字段n 字段n类型 [COMMENT 字段n注释 ]
) [ COMMENT 表注释 ] ;

例:创建一张表 tb_user ,对应的结构如下,那么建表语句为:

在这里插入图片描述

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

切换到itheima这个数据库下,再创建tb_user表:

在这里插入图片描述

2.2.2.2 查询表

查询表操作汇总:

# 查询当前数据库所有表
show tables;# 查看指定表结构
desc 表名;# 查询指定表的建表语句
show create table 表名;

1.查询当前数据库所有表

use sys;
show tables;

例:切换到sys这个系统数据库,并查看系统数据库中的所有表结构。

在这里插入图片描述

2.查看指定表结构和建表语句

例:切换到itheima数据库,并查看tb_user表结构和建表语句

use itheima;
desc tb_user;
show create table tb_user;

在这里插入图片描述

在这里插入图片描述

2.2.2.3 数据类型

创建表时,需要指定字段的数据类型,此处详细介绍一下MySQL的数据类型。

MySQL中的数据类型有很多,主要分为三类:

  • 数值类型
  • 字符串类型
  • 日期时间类型

1.数值类型

类型大小有符号(SIGNED)范围无符号(UNSIGNED)范围描述
TINYINT1 byte(-128,127)(0,255)小整数值
SMALLINT2 bytes(-32768,32767)(0,65535)大整数值
MEDIUMINT3 bytes(-8388608,8388607)(0,16777215)大整数值
INT或INTEGER4 bytes(-2147483648,2147483647)(0,4294967295)大整数值
BIGINT8 bytes(-263,263-1)(0,2^64-1)极大整数值
FLOAT4 bytes(-3.402823466 E+38,3.402823466351 E+38)0 和 (1.175494351 E-38,3.402823466 E+38)单精度浮点数值
DOUBLE8 bytes(-1.7976931348623157 E+308,1.7976931348623157 E+308)0 和 (2.2250738585072014 E-308,1.7976931348623157 E+308)双精度浮点数值
DECIMAL依赖于M(精度)和D(标度)的值依赖于M(精度)和D(标度)的值小数值(精确定点数)

例如:

  • 年龄字段 – 不会出现负数, 而且人的年龄不会太大 : age tinyint unsigned
  • 分数 – 总分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极大文本数据

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

例如:

  • 用户名 username – 长度不定, 最长不会超过50:username varchar(50)
  • 性别 gender – 存储值, 不是男,就是女:gender char(1)
  • 手机号 phone – 固定长度为11:phone char(11)

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混合日期和时间值,时间戳

例如:

  • 生日字段 birthday:birthday date
  • 创建时间 createtime:createtime datetime

案例:

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

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

对应的建表语句如下:

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 '员工表';

在这里插入图片描述

2.2.2.4 修改表

修改表操作汇总:

# 添加字段
ALTER TABLE 表名 ADD 字段名 类型 (长度) [ COMMENT 注释 ] [ 约束 ];# 修改数据类型
ALTER TABLE 表名 MODIFY 字段名 新数据类型 (长度);# 修改字段名和字段类型
ALTER TABLE 表名 CHANGE 旧字段名 新字段名 类型 (长度) [ COMMENT 注释 ] [ 约束 ];# 删除字段
ALTER TABLE 表名 DROP 字段名;# 修改表名
ALTER TABLE 表名 RENAME TO 新表名;

1.添加字段

例:为emp表增加一个新的字段”昵称”为nickname,类型为varchar(20)

alter table emp add nickname varchar(20) comment '昵称';

在这里插入图片描述

2.修改数据类型

例:修改emp表的nickname字段,将其数据类型为char(30)

alter table emp modify nickname char(30);

在这里插入图片描述

3.修改字段名和字段类型

例:将emp表的nickname字段修改为username,类型为varchar(30)

alter table emp change nickname username varchar(30) comment '用户名';

在这里插入图片描述

4.删除字段

例:删除emp表中的username字段

alter table emp drop username;

在这里插入图片描述

5.修改表名

例:将emp表重命名为employee

alter table emp rename to employee;

在这里插入图片描述

2.2.2.5 删除表

删除表操作汇总:

# 删除表
DROP TABLE [ IF EXISTS ] 表名;# 删除指定表, 并重新创建表
TRUNCATE TABLE 表名;

注:
1.在删除表的时候,表中的全部数据也都会被删除。
2.可选项 IF EXISTS ,代表只有表名存在时才会删除该表,表名不存在,则不执行删除操作。
3.使用TRUNCATE TABLE 表名会删除原有的表,并按原有表结构重新创建表,从结果上看,就是清空了表中的数据。

1.删除表

例:如果tb_user表存在,则删除tb_user表

drop table if exists tb_user;

在这里插入图片描述

2.删除指定表, 并重新创建表(清空表中的数据)

例:清空employee表中的数据

truncate table employee;

在这里插入图片描述

2.3 DML

DML:数据操作语言(Data Manipulation Language),用来对数据库表中的数据进行增删改。

2.3.1 DataGrip的安装与使用

MySQL的图形化界面工具,主要有SqlyogNavicatDataGrip

此处选择使用DataGrip,这种图形化界面工具,功能更加强大,界面提示更加友好。

1.下载与安装

百度网盘链接:https://pan.baidu.com/s/13QGW6bIcYwNn-jCiwtxHkg
提取码:odo5

2.安装

双击datagrip-2021.2.2.exe,一直点击Next即可。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

3.使用

创建新工程:New Project -> 输入工程名,此处我填写的工程名是 mysql-base

在这里插入图片描述
连接数据库:左上角加号 -> Database Source ->MySQL ->输入用户名密码,并下载驱动 -> 先点击Apply再OK

在这里插入图片描述

在这里插入图片描述
展示数据库:

在这里插入图片描述

创建数据库:右键MySQL - @localhost -> New -> Schema

在这里插入图片描述
在这里插入图片描述

创建表:右键数据库 -> New -> Table

在这里插入图片描述

在这里插入图片描述

编写SQl语句:右键数据库 -> New -> Query Console

在这里插入图片描述

在这里插入图片描述

2.3.2 添加数据

添加表数据的操作汇总:

# 给指定字段添加数据
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, ...);

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

1.给指定字段添加数据

例1:给employee表的id、name、gender、age字段添加数据

insert into employee (id,name,gender,age) values (2,'张三丰','男',100);

例2:给employee表所有的字段添加数据

insert into employee (id, workno, name, gender, age, idcard, entrydate)
values (1, '2', '张无忌', '男', 18, '123456789012345670', '2005-01-01');

2.给全部字段添加数据

例:给employee表全部字段添加数据

insert into employee values (1,'2','张无忌','男',18,'123456789012345670','2005-01-01');

3.批量添加数据

例:批量插入两条数据到employee表

insert into employee
values (3, '3', '周芷若', '女', 18, '123456789012345670', '2005-01-01'),(4, '4', '赵敏', '女', 18, '123456789012345670', '2005-01-01');

2.3.3 修改数据

修改表数据的语法如下:

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

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

例1:修改id为1的数据,将name修改为itheima

update employee set name ='itheima' where id = 1;

例2:修改id为1的数据, 将name修改为小昭, gender修改为 女

update employee set name = '小昭',gender = '女' where id = 1;

例3:将所有的员工入职日期修改为 2008-01-01

update employee set entrydate = '2008-01-01';

2.3.4 删除数据

删除数据的语法如下:

DELETE FROM 表名 [ WHERE 条件 ];

注:
1.DELETE 语句的条件可以有,也可以没有,如果没有条件,则会删除整张表的所有数据
2.DELETE 语句不能删除某一个字段的值(可以使用UPDATE,将该字段值置为NULL即可)

例1:删除gender为女的员工

delete from employee where gender = '女';

例2:删除所有员工

delete from employee;

2.4 DQL

DQL:数据查询语言(Data Query Language),用来查询数据库中表的记录

2.4.1 基本语法

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

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

完整语法可以拆分为以下几个部分:

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

要演示DQL语句,首先要准备一些数据如下:

-- 删除emp表
drop table if exists emp;-- 创建emp表
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 '身份证号',workaddress varchar(50) comment '工作地址',entrydate   date comment '入职时间'
) comment '员工表';-- 插入数据
insert into emp (id, workno, name, gender, age, idcard, workaddress, entrydate)
values (1, '1', '柳岩', '女', 20, '123456789012345678', '北京', '2000-01-01'),(2, '2', '张无忌', '男', 18, '123456789012345670', '北京', '2005-09-01'),(3, '3', '韦一笑', '男', 38, '123456789012345671', '上海', '2005-08-01'),(4, '4', '赵敏', '女', 18, '123456789012345672', '北京', '2009-12-01'),(5, '5', '小昭', '女', 16, '123456789012345673', '上海', '2007-07-01'),(6, '6', '杨逍', '男', 28, '123456789012345674', '北京', '2006-01-01'),(7, '7', '范瑶', '男', 40, '123456789012345675', '北京', '2005-05-01'),(8, '8', '黛绮丝', '女', 38, '123456789012345676', '天津', '2015-05-01'),(9, '9', '范凉凉', '女', 45, '123456789012345677', '北京', '2010-04-01'),(10, '10', '陈友谅', '男', 53, '123456789012345679', '上海', '2011-01-01'),(11, '11', '张士诚', '男', 55, '12345678901234567X', '江苏', '2015-05-01'),(12, '12', '常遇春', '男', 32, '123456789012345610', '北京', '2004-02-01'),(13, '13', '张三丰', '男', 88, '123456789012345611', '江苏', '2020-11-01'),(14, '14', '灭绝', '女', 65, '123456789012345612', '西安', '2019-05-01'),(15, '15', '胡青牛', '男', 70, '123456789012345613', '西安', '2018-04-01'),(16, '16', '周芷若', '女', 18, null, '北京', '2012-06-01');

执行后查询表中数据,内容如下:

在这里插入图片描述

2.4.2 基础查询

基础查询,即不带任何的查询条件,语法汇总如下:

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

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

案例:

1.查询指定字段 name, workno, age并返回

select name,workno,age from emp;

2.查询返回所有字段

select * from emp;
select id, workno, name, gender, age, idcard, workaddress, entrydate from emp;

3.查询所有员工的工作地址,起别名

select workaddress as '工作地址' from emp;
# as可以省略
select workaddress '工作地址' from emp;

4.查询公司员工的上班地址有哪些(不要重复)

select distinct workaddress from emp;

2.4.3 条件查询

语法:

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

比较运算符:

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

逻辑运算符:

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

案例:
1.查询年龄等于 88 的员工

select * from emp where age = 88;

2.查询年龄小于 20 的员工信息

select * from emp where age < 20;

3.查询年龄小于等于 20 的员工信息

select * from emp where age <= 20;

4.查询没有身份证号的员工信息

select * from emp where idcard is null;

5.查询有身份证号的员工信息

select * from emp where idcard is not null;

6.查询年龄不等于 88 的员工信息

select * from emp where age != 88;
select * from emp where age <> 88;

7.查询年龄在15岁(包含) 到 20岁(包含)之间的员工信息

select * from emp where age >= 15 and age <=20;
select * from emp where age >= 15 && age <=20;
select * from emp where age between 15 and 20;

8.查询性别为 女 且年龄小于 25岁的员工信息

select * from emp where gender = '女' and age < 25;

9.查询年龄等于18 或 20 或 40 的员工信息

select * from emp where age = 18 or age = 20 or age = 40;
select * from emp where age in(18,20,40);

10.查询姓名为两个字的员工信息

select * from emp where name like '__';

11.查询身份证号最后一位是X的员工信息

select * from emp where idcard like '%X'

2.4.4 聚合函数

聚合函数:将一列数据作为一个整体,进行纵向计算。

聚合函数描述
count统计数量
max最大值
min最小值
avg平均值
sum求和

语法如下:

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

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

案例:

1.统计该企业员工数量

select count(*) from emp; -- 统计的是总记录数
select count(idcard) from emp; -- 统计的是idcard字段不为null的记录数

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

select count(1) from emp;

2.统计该企业员工的平均年龄

select avg(age) from emp;

3.统计该企业员工的最大年龄

select max(age) from emp;

4.统计该企业员工的最小年龄

select min(age) from emp;

5.统计西安地区员工的年龄之和

select sum(age) from emp where workaddress = '西安';

2.4.5 分组查询

语法:

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

wherehaving 区别:

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

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

案例:

1.根据性别分组 , 统计男性员工 和 女性员工的数量

select gender,count(*) from emp group by gender;

在这里插入图片描述

注:只要出现统计数量这个词,就要想到 count(*)

2.根据性别分组 , 统计男性员工 和 女性员工的平均年龄

select gender,avg(age) from emp group by gender;

在这里插入图片描述

3.查询年龄小于45的员工 , 并根据工作地址分组 , 获取员工数量大于等于3的工作地址

select workaddress,count(*) address_ount from emp where age < 45 
group by workaddress having address_ount >=3;

在这里插入图片描述

4.统计各个工作地址上班的男性及女性员工的数量

select workaddress,gender,count(*) '数量' from emp group by workaddress,gender;

解释:统计了工作地址,又统计了男性和女性,所以此处以 workaddressgender 两个字段进行分组

在这里插入图片描述

2.4.6 排序查询

语法:

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

排序方式:

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

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

案例:

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;

2.4.7 分页查询

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

在这里插入图片描述

语法:

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

注:
1.起始索引从0开始,起始索引 = (查询页码 - 1)* 每页显示记录数
2.分页查询是数据库的方言,不同的数据库有不同的实现,MySQL中是LIMIT
3.如果起始索引是0,那么可以省略,直接简写为 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;

解释:由于每页展示10条数据,也就是第一页数据的索引值分别为0-9,而第二页数据的索引值为10-19,此处第二页的第一条数据为起始索引值,就是10,又要查完整的第二页10条数据,因此 limit 10,10

3.查询年龄第二大的员工

select * from emp order by age desc limit 1,1;

解释:以年龄作为降序,第二条数据即为年龄第二大的员工,第二条数据的索引值为1,然后只查这一条数据,因此为 limit 1,1

2.4.8 执行顺序

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

结论如下:from > where > group by > having > select > order by > limit

在这里插入图片描述

2.4.9 综合案例

1.查询年龄为20,21,22,23岁的员工信息

select * from emp where age in(20,21,22,23);

2.查询性别为 男 ,并且年龄在 20-40 岁(含)以内的姓名为三个字的员工

select * from emp where gender = '男' and ( age between 20 and 40 ) and name like '___';

3.统计员工表中, 年龄小于60岁的 , 男性员工和女性员工的人数

select gender,count(*) '员工人数' from emp where age < 60 group by gender;

4.查询所有年龄小于等于35岁员工的姓名和年龄,并对查询结果按年龄升序排序,如果年龄相同按入职时间降序排序

select name,age from emp where age <=35 order by age asc,entrydate desc;

5.查询性别为男,且年龄在20-40 岁(含)以内的前5个员工信息,对查询的结果按年龄升序排序,年龄相同按入职时间升序排序

select * from emp where gender = '男' and ( age between 20 and 40 )
order by age, entrydate limit 5;

2.5 DCL

DCL:数据控制语言(Data Control Language),用来创建数据库用户、控制数据库的访问权限。

2.5.1 管理用户

管理用户的操作汇总如下:

# 查询用户
select * from mysql.user;# 创建用户
CREATE USER '用户名'@'主机名' IDENTIFIED BY '密码';# 修改用户密码
ALTER USER '用户名'@'主机名' IDENTIFIED WITH mysql_native_password BY '新密码';# 删除用户
DROP USER '用户名'@'主机名';

注:
1.在MySQL中需要通过用户名@主机名的方式,来唯一标识一个用户
2.主机名可以使用 % 通配,表示任意主机都可以访问该数据库
3.这类SQL语句开发人员操作的比较少,主要是DBA( Database Administrator 数据库
管理员)使用

案例:

1.查询用户

select * from mysql.user;

结果如下:

在这里插入图片描述

其中 Host 字段代表当前用户访问的主机, 如果为 localhost,仅代表只能够在当前本机访问,是不可以
远程访问的。 User 字段代表的是访问该数据库的用户名。在MySQL中需要通过Host和User来唯一标识一
个用户。

2.创建用户itcast, 只能够在当前主机localhost访问, 密码123456

create user 'itcast'@'localhost' identified by '123456';

3.创建用户heima, 可以在任意主机访问该数据库, 密码123456

create user 'heima'@'%' identified by '123456';

4.修改用户heima的访问密码为1234

alter user 'heima'@'%' identified with mysql_native_password by '1234';

5.删除 itcast@localhost 用户

drop user 'itcast'@'localhost';

2.5.2 权限控制

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

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

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

权限控制的操作汇总如下:

# 查询权限
SHOW GRANTS FOR '用户名'@'主机名';# 授予权限
GRANT 权限列表 ON 数据库名.表名 TO '用户名'@'主机名';# 撤销权限
REVOKE 权限列表 ON 数据库名.表名 FROM '用户名'@'主机名';

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

案例:

1.查询 'heima'@'%' 用户的权限

show grants for 'heima'@'%';

2.授予 'heima'@'%' 用户itcast数据库所有表的所有操作权限

grant all on itcast.* to 'heima'@'%';

3.撤销 'heima'@'%' 用户的itcast数据库的所有权限

revoke all on itcast.* from 'heima'@'%';

3. 函数

函数:是指一段可以直接被另一段程序调用的程序或代码。

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

3.1 字符串函数

常用的字符串函数如下:

函数功能
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个长度的字符串
# concat : 字符串拼接
select concat('Hello' , ' MySQL'); -- Hello MySQL# lower : 全部转小写
select lower('Hello'); -- hello# upper : 全部转大写
select upper('Hello'); -- HELLO# lpad : 左填充
select lpad('01', 5, '-'); -- ---01# rpad : 右填充
select rpad('01', 5, '-'); -- 01---# trim : 去除空格
select trim(' Hello MySQL '); -- Hello MySQL# substring : 截取子字符串
select substring('Hello MySQL',1,5); -- Hello

案例:

由于业务需求变更,企业员工的工号,统一为5位数,目前不足5位数的全部在前面补0。比如: 1号员工的工号应该为00001。

update emp set workno = lpad(workno, 5, '0');

在这里插入图片描述

3.2 数值函数

常用的数值函数如下:

函数功能
CEIL(x)向上取整
FLOOR(x)向下取整
MOD(x,y)返回x/y的模
RAND()返回0~1内的随机数
ROUND(x,y)求参数x的四舍五入的值,保留y位小数
# ceil:向上取整
select ceil(1.1); -- 2# floor:向下取整
select floor(1.9); -- 1# mod:取模
select mod(7,4); -- 3# rand:获取随机数
select rand(); -- 0.6382742446272394# round:四舍五入
select round(2.344,2); -- 2.34

案例:

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

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

select lpad(round(rand()*1000000 , 0), 6, '0');

3.3 日期函数

常用的日期函数如下:

函数功能
CURDATE()返回当前日期
CURTIME()返回当前时间
NOW()返回当前日期和时间
YEAR(date)获取指定date的年份
MONTH(date)获取指定date的月份
DAY(date)获取指定date的日期
DATE_ADD(date, INTERVAL expr type)返回一个日期/时间值加上一个时间间隔expr后的时间值
DATEDIFF(date1,date2)返回起始时间date1 和 结束时间date2之间的天数
# curdate:当前日期
select curdate(); -- 2023-05-16# curtime:当前时间
select curtime(); -- 22:55:32# now:当前日期和时间
select now(); -- 2023-05-16 22:55:39# YEAR , MONTH , DAY:当前年、月、日
select YEAR(now()); -- 2023
select MONTH(now()); -- 5
select DAY(now()); -- 16# date_add:增加指定的时间间隔
select date_add(now(), INTERVAL 70 YEAR ); -- 2093-05-16 22:56:04# datediff:获取两个日期相差的天数
select datediff('2021-10-01', '2021-12-01'); -- -61

案例:

查询所有员工的入职天数,并根据入职天数倒序排序。

思路: 入职天数,就是通过当前日期 - 入职日期,所以需要使用datediff函数来完成。

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默认值

1.if

select if(true, 'Ok', 'Error'); -- Ok
select if(false, 'Ok', 'Error'); -- Error

2.ifnull

select ifnull('Ok','Default'); -- Ok
select ifnull('','Default'); -- 空字符串
select ifnull(null,'Default'); -- Default
select ifnull(null, 0); -- 0

3.case when then else end

需求: 查询emp表的员工姓名和工作地址 (北京/上海 ----> 一线城市 , 其他 ----> 二线城市)

select name,(case workaddresswhen '北京' then '一线城市'when '上海' then '一线城市'else '二线城市' end) as '工作地址'
from emp;

在这里插入图片描述

案例:根据学科的分数来评定等级,其中分数在85(包含)以上为优秀,60(包含)以上为及格,低于60不及格。

准备数据如下:

-- 创建学员成绩表
create table score
(id      int comment 'ID',name    varchar(20) comment '姓名',math    int comment '数学',english int comment '英语',chinese int comment '语文'
) comment '学员成绩表';-- 插入数据
insert into score(id, name, math, english, chinese)
VALUES (1, 'Tom', 67, 88, 95),(2, 'Rose', 23, 66, 90),(3, 'Jack', 56, 98, 76);

SQL语句如下:

select id,name,(case when math >= 85 then '优秀' when math >= 60 then '及格' else '不及格' end)       '数学',(case when english >= 85 then '优秀' when english >= 60 then '及格' else '不及格' end) '英语',(case when chinese >= 85 then '优秀' when chinese >= 60 then '及格' else '不及格' end) '语文'
from score;

在这里插入图片描述

4. 约束

4.1 基本约束

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

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

分类:

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

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

以下对除了外键约束以外的约束进行演示:

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

在这里插入图片描述

根据以上约束,对应的建表语句如下:

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

验证约束是否生效:

insert into tb_user(name, age, status, gender)values ('Tom1', 19, '1', '男'), ('Tom2', 25, '0', '男');
insert into tb_user(name, age, status, gender) values ('Tom3', 19, '1', '男');
insert into tb_user(name, age, status, gender) values (null, 19, '1', '男');
insert into tb_user(name, age, status, gender) values ('Tom3', 19, '1', '男');
insert into tb_user(name, age, status, gender) values ('Tom4', 80, '1', '男');
insert into tb_user(name, age, status, gender) values ('Tom5', -1, '1', '男');
insert into tb_user(name, age, status, gender) values ('Tom5', 121, '1', '男');
insert into tb_user(name, age, gender) values ('Tom5', 120, '男');

4.2 外键约束

4.2.1 介绍

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

举例如下:
在这里插入图片描述

左侧的emp表是员工表,里面存储员工的基本信息,包含员工的ID、姓名、年龄、职位、薪资、入职日期、上级主管ID、部门ID,在员工的信息中存储的是部门的ID dept_id,而这个部门的ID是关联的部门表dept的主键id,那emp表的dept_id就是外键,关联的是另一张表的主键。

注:
1.目前上述两张表,只是在逻辑上存在这样一层关系;在数据库层面,并未建立外键关联,所以是无法保证数据的一致性和完整性的。
2.添加外键约束的表为子表(从表),外键所关联的表为父表(主表)

在没有数据库外键关联的情况下,能够保证一致性和完整性呢?我们来测试一下。

准备数据:

-- 删除dept表
drop table if exists dept;-- 创建dept表
create table dept
(id   int auto_increment comment 'ID' primary key,name varchar(50) not null comment '部门名称'
) comment '部门表';-- 向dept表插入数据
INSERT INTO dept (id, name)
VALUES (1, '研发部'),(2, '市场部'),(3, '财务部'),(4, '销售部'),(5, '总经办');-- 删除emp表
drop table if exists emp;-- 创建emp表
create table emp
(id        int auto_increment comment 'ID' primary key,name      varchar(50) not null comment '姓名',age       int comment '年龄',job       varchar(20) comment '职位',salary    int comment '薪资',entrydate date comment '入职时间',managerid int comment '直属领导ID',dept_id   int comment '部门ID'
) comment '员工表';-- 向emp表插入数据
INSERT INTO emp (id, name, age, job, salary, entrydate, managerid, dept_id)
VALUES (1, '金庸', 66, '总裁', 20000, '2000-01-01', null, 5),(2, '张无忌', 20, '项目经理', 12500, '2005-12-05', 1, 1),(3, '杨逍', 33, '开发', 8400, '2000-11-03', 2, 1),(4, '韦一笑', 48, '开发', 11000, '2002-02-05', 2, 1),(5, '常遇春', 43, '开发', 10500, '2004-09-07', 3, 1),(6, '小昭', 19, '程序员鼓励师', 6600, '2004-10-12', 2, 1);

得到表数据如下:
在这里插入图片描述

接下来,我们可以做一个测试,删除id为1的部门信息。

在这里插入图片描述
结果,我们看到删除成功,而删除成功之后,部门表不存在id为1的部门,而在emp表中还有很多的员工,关联的为id为1的部门,此时就出现了数据的不完整性。 而要想解决这个问题就得通过数据库的外键约束。

4.2.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);

在这里插入图片描述
添加了外键约束之后,我们再到dept表(父表)删除id为1的记录,然后看一下会发生什么现象。 此时
将会报错,不能删除或更新父表记录,因为存在外键约束。

delete from dept where id = 1;

在这里插入图片描述

2.删除外键

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

例:删除emp表的外键fk_emp_dept_id。

alter table emp drop foreign key fk_emp_dept_id;

4.2.3 删除/更新行为

添加了外键之后,再删除父表数据时产生的约束行为,我们就称为删除/更新行为。

具体的删除/更新行为有以下几种:

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

语法:

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

由于NO ACTION 是默认行为,我们前面语法演示的时候,已经测试过了,就不再演示了,这里我们再演示其他的两种行为:CASCADESET NULL

1.CASCADE

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

例1:修改父表id为1的记录,将id修改为6

在这里插入图片描述

我们发现,原来在子表中dept_id值为1的记录,现在也变为6了,这就是cascade级联的效果。

注:在一般的业务系统中,不会修改一张表的主键值。

例2:删除父表id为6的记录

在这里插入图片描述
我们发现,父表的数据删除成功了,但是子表中关联的记录也被级联删除了。

2.SET NULL

在进行测试之前,我们先需要删除上面建立的外键 fk_emp_dept_id。然后再通过数据脚本,将
emp、dept表的数据恢复了。

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

接下来,我们删除id为1的数据,看看会发生什么样的现象。

在这里插入图片描述
我们发现父表的记录是可以正常的删除的,父表的数据删除之后,再打开子表 emp,我们发现子表emp的dept_id字段,原来dept_id为1的数据,现在都被置为NULL了。

在这里插入图片描述

5. 多表查询

5.1 多表关系

项目开发中,在进行数据库表结构设计时,会根据业务需求及业务模块之间的关系,分析并设计表结构,由于业务之间相互关联,所以各个表结构之间也存在着各种联系,基本上分为三种:

  • 一对多(多对一)
  • 多对多
  • 一对一

5.1.1 一对多

  • 案例:部门 与 员工的关系
  • 关系:一个部门对应多个员工,一个员工对应一个部门
  • 实现:在多的一方建立外键,指向一的一方的主键

在这里插入图片描述

5.1.2 多对多

  • 案例:学生 与 课程的关系
  • 关系:一个学生可以选修多门课程,一门课程也可以供多个学生选择
  • 实现:建立第三张中间表,中间表至少包含两个外键,分别关联两方主键

在这里插入图片描述

对应的SQL脚本:

-- 创建学生表
create table student
(id   int auto_increment primary key comment '主键ID',name varchar(10) comment '姓名',no   varchar(10) comment '学号'
) comment '学生表';-- 向学生表插入数据
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 '课程表';-- 向课程表插入数据
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 '学生课程中间表';-- 向学生课程关系表插入数据
insert into student_course
values (null, 1, 1),(null, 1, 2),(null, 1, 3),(null, 2, 2),(null, 2, 3),(null, 3, 4);

5.1.3 一对一

  • 案例:用户 与 用户详情的关系
  • 关系:一对一关系,多用于单表拆分,将一张表的基础字段放在一张表中,其他详情字段放在另一张表中,以提升操作效率
  • 实现:在任意一方加入外键,关联另外一方的主键,并且设置外键为唯一的(UNIQUE)

在这里插入图片描述

对应的SQL脚本:

-- 创建用户基本信息表
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 '用户基本信息表';-- 向用户基本信息表插入数据
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');-- 创建用户教育信息表
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_edu(id, degree, major, primaryschool, middleschool, university, userid)
values (null, '本科', '舞蹈', '静安区第一小学', '静安区第一中学', '北京舞蹈学院', 1),(null, '硕士', '表演', '朝阳区第一小学', '朝阳区第一中学', '北京电影学院', 2),(null, '本科', '英语', '杭州市第一小学', '杭州市第一中学', '杭州师范大学', 3),(null, '本科', '应用数学', '阳泉第一小学', '阳泉区第一中学', '清华大学', 4);

5.2 多表查询概述

5.2.1 数据准备

1.删除之前 emp, dept表的测试数据

-- 删除emp表
drop table if exists emp;-- 删除dept表
drop table if exists dept;

2.执行如下脚本,创建emp表与dept表并插入测试数据

-- 创建dept表,并插入数据
create table dept
(id   int auto_increment comment 'ID' primary key,name varchar(50) not null comment '部门名称'
) comment '部门表';INSERT INTO dept (id, name)
VALUES (1, '研发部'),(2, '市场部'),(3, '财务部'),(4, '销售部'),(5, '总经办'),(6, '人事部');-- 创建emp表,并插入数据
create table emp
(id        int auto_increment comment 'ID' primary key,name      varchar(50) not null comment '姓名',age       int comment '年龄',job       varchar(20) comment '职位',salary    int comment '薪资',entrydate date comment '入职时间',managerid int comment '直属领导ID',dept_id   int comment '部门ID'
) comment '员工表';-- 添加外键
alter table emp add constraint fk_emp_dept_id foreign key (dept_id) references dept(id);INSERT INTO emp (id, name, age, job, salary, entrydate, managerid, dept_id)
VALUES (1, '金庸', 66, '总裁', 20000, '2000-01-01', null, 5),(2, '张无忌', 20, '项目经理', 12500, '2005-12-05', 1, 1),(3, '杨逍', 33, '开发', 8400, '2000-11-03', 2, 1),(4, '韦一笑', 48, '开发', 11000, '2002-02-05', 2, 1),(5, '常遇春', 43, '开发', 10500, '2004-09-07', 3, 1),(6, '小昭', 19, '程序员鼓励师', 6600, '2004-10-12', 2, 1),(7, '灭绝', 60, '财务总监', 8500, '2002-09-12', 1, 3),(8, '周芷若', 19, '会计', 4800, '2006-06-02', 7, 3),(9, '丁敏君', 23, '出纳', 5250, '2009-05-13', 7, 3),(10, '赵敏', 20, '市场部总监', 12500, '2004-10-12', 1, 2),(11, '鹿杖客', 56, '职员', 3750, '2006-10-03', 10, 2),(12, '鹤笔翁', 19, '职员', 3750, '2007-05-09', 10, 2),(13, '方东白', 19, '职员', 5500, '2009-02-12', 10, 2),(14, '张三丰', 88, '销售总监', 14000, '2004-10-12', 1, 4),(15, '俞莲舟', 38, '销售', 4600, '2004-10-12', 14, 4),(16, '宋远桥', 40, '销售', 4600, '2004-10-12', 14, 4),(17, '陈友谅', 42, null, 2000, '2011-10-12', 1, null);      

5.2.2 概述

多表查询就是指从多张表中查询数据。

原来查询单表数据,执行的SQL形式为:select * from emp;,现在要执行多表查询,就只需要使用逗号分隔多张表即可,如: select * from emp , dept;,具体的执行结果如下:

在这里插入图片描述
此时,我们看到查询结果中包含了大量的结果集,总共102条记录,而这其实就是员工表emp所有的记录
(17条) 与 部门表dept所有记录(6条) 的所有组合情况,这种现象称之为笛卡尔积。接下来,就来简单
介绍下笛卡尔积。

笛卡尔积: 笛卡尔乘积是指在数学中,两个集合A集合 和 B集合的所有组合情况。

在这里插入图片描述

而在多表查询中,我们是需要消除无效的笛卡尔积的,只保留两张表关联部分的数据。

在这里插入图片描述

在这里插入图片描述

在SQL语句中,如何来去除无效的笛卡尔积呢? 我们可以给多表查询加上连接查询的条件即可。
select * from emp , dept where emp.dept_id = dept.id;

在这里插入图片描述
由于id为17的员工,没有dept_id字段值,所以在多表查询时,根据连接查询的条件并没有查询到。

5.2.3 分类

多表查询分为连接查询和子查询:

子查询:SQL语句中嵌套SELECT语句,称为嵌套查询,又称子查询。

连接查询:

  • 内连接:相当于查询A、B交集部分数据
  • 左外连接:查询左表所有数据,以及两张表交集部分数据
  • 右外连接:查询右表所有数据,以及两张表交集部分数据
  • 自连接:当前表与自身的连接查询,自连接必须使用表别名

在这里插入图片描述

5.3 内连接

在这里插入图片描述

内连接查询的是两张表交集部分的数据(也就是绿色部分的数据),内连接的语法分为两种: 隐式内连接和显式内连接。

语法:

-- 隐式内连接
SELECT 字段列表 FROM1 ,2 WHERE 条件 ... ;-- 显式内连接
SELECT 字段列表 FROM1 [ INNER ] JOIN2 ON 连接条件 ... ;

案例:

例1:查询每一个员工的姓名 , 及关联的部门的名称 (隐式内连接实现)

表结构: emp , dept
连接条件: emp.dept_id = dept.id

select emp.name , dept.name from emp , dept where emp.dept_id = dept.id ;
-- 为每一张表起别名,简化SQL编写
select e.name,d.name from emp e,dept d where e.dept_id = d.id;

注:一旦为表起了别名,就不能再使用表名来指定对应的字段了,此时只能够使用别名来指定字段。(因为执行顺序 from > where

例2:查询每一个员工的姓名 , 及关联的部门的名称 (显式内连接实现)

表结构: emp , dept
连接条件: emp.dept_id = dept.id

select e.name,d.name from emp e inner join dept d on e.dept_id = d.id;

5.4 外连接

在这里插入图片描述

外连接分为两种,分别是:左外连接 和 右外连接。

语法:.

-- 左外连接
SELECT 字段列表 FROM1 LEFT [ OUTER ] JOIN2 ON 条件 ... ;-- 右外连接
SELECT 字段列表 FROM1 RIGHT [ OUTER ] JOIN2 ON 条件 ... ;


1.左外连接相当于查询表1(左表)的所有数据,当然也包含表1和表2交集部分的数据。
2.右外连接相当于查询表2(右表)的所有数据,当然也包含表1和表2交集部分的数据。

案例:

例1:查询emp表的所有数据, 和对应的部门信息

由于需求中提到,要查询emp的所有数据,所以是不能内连接查询的,需要考虑使用外连接查询。
表结构:emp, dept
连接条件:emp.dept_id = dept.id

select e.*,d.name from emp e left join dept d on e.dept_id = d.id;

注:当出现需要查询某张表的所有数据时,要想到左外连接。

例2:查询dept表的所有数据, 和对应的员工信息(右外连接)

表结构:emp, dept
连接条件:emp.dept_id = dept.id

select d.*,e.* from emp e right join dept d on e.dept_id = d.id;

右外连接也可以用左外连接来实现,只需要调换两张表的位置即可:

select d.*,e.* from dept d left join emp e on e.dept_id = d.id;

注:左外连接和右外连接是可以相互替换的,而我们在日常开发使用时,更偏向于左外连接。

5.5 自连接

5.5.1 自连接查询

自连接查询,顾名思义,就是自己连接自己,也就是把一张表连接查询多次;

对于自连接查询,可以是内连接查询,也可以是外连接查询。

语法如下:

SELECT 字段列表 FROM 表A 别名A JOIN 表A 别名B ON 条件 ... ;

案例:

例1:查询员工 及其 所属领导的名字

表结构: emp

-- 此时把a表视为员工表,b表视为领导表
select a.name '员工',b.name '领导' from emp a,emp b where a.managerid = b.id;

注:
1.此处的特殊之处在于,领导也是员工,所以员工表和领导表是一张表
2.连接条件 a.managerid = b.id,表示员工表的领导的id,就是领导表的id
3.a.managerid = b.id 该连接条件不能写成 a.id= b.managerid,否则此时a就成了领导表,b为员工表

例2:查询所有员工 emp 及其领导的名字 emp , 如果员工没有领导, 也需要查询出来

表结构: emp a , emp b

select a.name '员工',b.name '领导' from emp a left join emp b on a.managerid = b.id;

注:在自连接查询中,必须要为表起别名,否则我们将不清楚所指定的条件、返回的字段,到底是哪一张表的字段。

5.5.2 联合查询

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

语法:

SELECT 字段列表 FROM 表A ... 
UNION [ ALL ] 
SELECT 字段列表 FROM 表B ....;

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

案例:将薪资低于 5000 的员工 , 和 年龄大于 50 岁的员工全部查询出来。

当前对于这个需求,我们可以直接使用多条件查询,使用逻辑运算符 or 连接即可。 那这里呢,我们
也可以通过 union/union all 来联合查询

select * from emp where salary < 5000
union all
select * from emp where age > 50;

在这里插入图片描述
由此可见,union all查询出来的结果,仅仅进行简单的合并,并未去重。

select * from emp where salary < 5000
union
select * from emp where age > 50;

在这里插入图片描述
union 联合查询,会对查询出来的结果进行去重处理。

5.6 子查询

5.6.1 概述

概念:SQL语句中嵌套SELECT语句,称为嵌套查询,又称子查询

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

注:子查询外部的语句可以是 INSERT / UPDATE / DELETE / SELECT 的任何一个

分类:

根据子查询结果不同,分为:

  • 标量子查询(子查询结果为单个值)
  • 列子查询(子查询结果为一列)
  • 行子查询(子查询结果为一行)
  • 表子查询(子查询结果为多行多列)

根据子查询位置,分为:

  • WHERE之后
  • FROM之后
  • SELECT之后

5.6.2 标量子查询

标量子查询:子查询返回的结果是单个值(数字、字符串、日期等),最简单的形式,这种子查询称为标量子查询。

常用的操作符:= <> > >= < <=

案例:

例1:查询 “销售部” 的所有员工信息

完成这个需求时,我们可以将需求分解为两步:

① 查询销售部的信息(部门ID)

select id from dept where name = '销售部';

在这里插入图片描述

② 根据 “销售部” 部门ID,查询员工信息

select * from emp where dept_id = 4;

由于4 是由 select id from dept where name = '销售部'查询得到的,所以将4替换成该语句即可。

select * from emp where dept_id = ( select id from dept where name = '销售部' );

注:做子查询的题目时,需要将需求分解,分步查询然后再合并。

例2:查询在 “方东白” 入职之后的员工信息

① 查询 方东白 的入职日期

select entrydate from emp where name = '方东白';

在这里插入图片描述

② 查询指定入职日期之后入职的员工信息

select * from emp where entrydate > '2009-02-12';

由于2009-02-12 是由 select entrydate from emp where name = '方东白'查询得到的,所以将2009-02-12替换成该语句即可。

select * from emp where entrydate > ( select entrydate from emp where name = '方东白' );

5.6.3 列子查询

子查询返回的结果是一列(可以是多行),这种子查询称为列子查询。

常用的操作符:IN 、NOT IN 、 ANY 、SOME 、 ALL

操作符描述
IN 在指定的集合范围之内,多选一
NOT IN不在指定的集合范围之内
ANY子查询返回列表中,有任意一个满足即可
SOME与ANY等同,使用SOME的地方都可以使用ANY
ALL子查询返回列表的所有值都必须满足

案例:

例1:查询 “销售部” 和 “市场部” 的所有员工信息

① 查询 “销售部” 和 “市场部” 的部门ID

select id from dept where name in('销售部','市场部');

在这里插入图片描述

② 根据部门ID, 查询员工信息

select * from emp where dept_id in(2,4);
select * from emp where dept_id in ( select id from dept where name in('销售部','市场部') );

例2:查询比 财务部 所有人工资都高的员工信息

① 查询所有 财务部 人员工资

-- 查询财务部的部门id
select id from dept where name = '财务部';-- 查询财务部所有员工工资
select salary from emp where dept_id = ( select id from dept where 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 = '财务部' ));

例3:查询比研发部其中任意一人工资高的员工信息

① 查询研发部所有人工资

-- 查询研发部的部门id
select id from dept where name = '研发部';-- 查询研发部所有员工工资
select 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 = '研发部' ) );

比研发部其中任意一人工资高,等价于比研发部最低工资的人高就行,因此也可以改成如下语句:

select * from emp
where salary > ( select min(salary) from emp where dept_id = ( select id from dept where name = '研发部' ) );

5.6.4 行子查询

子查询返回的结果是一行(可以是多列),这种子查询称为行子查询。

常用的操作符:= 、<> 、IN 、NOT IN

案例:查询与 “张无忌” 的薪资及直属领导相同的员工信息

① 查询 “张无忌” 的薪资及直属领导

select salary,managerid from emp where name = '张无忌';

在这里插入图片描述

② 查询与 “张无忌” 的薪资及直属领导相同的员工信息

select * from emp where (salary,managerid) = ('12500',1);
select * from emp where (salary,managerid) = ( select salary,managerid from emp where name = '张无忌' );

5.6.5 表子查询

子查询返回的结果是多行多列,这种子查询称为表子查询。

常用的操作符:IN

案例:

例1:查询与 “鹿杖客” , “宋远桥” 的职位和薪资相同的员工信息

① 查询 “鹿杖客” , “宋远桥” 的职位和薪资

select job,salary from emp where name in('鹿杖客','宋远桥');

在这里插入图片描述

② 查询与 “鹿杖客” , “宋远桥” 的职位和薪资相同的员工信息

select * from emp where (job,salary) in (('职员',3750),('销售',4600));
select * from emp where (job,salary) in ( select job,salary from emp where name in('鹿杖客','宋远桥') );

例2:查询入职日期是 “2006-01-01” 之后的员工信息 , 及其部门信息

① 入职日期是 “2006-01-01” 之后的员工信息

select * from emp where entrydate > '2006-01-01';

在这里插入图片描述

② 查询这部分员工,对应的部门信息

select e.*, d.name
from (select * from emp where entrydate > '2006-01-01') e left join dept d on e.dept_id = d.id;

注:select * from emp where entrydate > '2006-01-01'查询出来的结果为新的子表,然后再对子表再进行新的查询

5.7 综合案例

数据准备:

-- 创建薪资等级表
create table salgrade
(grade int, -- 薪资等级losal int, -- 最低薪资hisal int  -- 最高薪资
) comment '薪资等级表';-- 插入数据
insert into salgrade values (1, 0, 3000); -- 表示1级的薪资范围为[0,3000]
insert into salgrade values (2, 3001, 5000);
insert into salgrade values (3, 5001, 8000);
insert into salgrade values (4, 8001, 10000);
insert into salgrade values (5, 10001, 15000);
insert into salgrade values (6, 15001, 20000);
insert into salgrade values (7, 20001, 25000);
insert into salgrade values (8, 25001, 30000);

注:以下例题中有 *的需要重点去看

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

select distinct d.id , d.name from emp e , dept d where e.dept_id = d.id;

解法二:子查询

-- 查询拥有部门ID的员工,并对部门ID去重,得到的就是拥有员工的部门ID
select distinct dept_id from emp where dept_id is not null;-- 根据得到的部门ID,去查部门信息
select id,name from dept where id in( select distinct dept_id from emp where dept_id is not null );

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 s.hisal;

6.查询 “研发部” 所有员工的信息及 工资等级 *

解法一:内连接 + 查询条件
表: emp , salgrade , dept
连接条件 : emp.salary between salgrade.losal and salgrade.hisal ,emp.dept_id = dept.id
查询条件 : dept.name = ‘研发部’

select e.*,d.name,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 = '研发部';

解法二:子查询 + 内连接
子查询: 研发部员工子表 (select * from emp where dept_id = ( select id from dept where name = ‘研发部’ )) emp
表: emp , salgrade , dept
连接条件 : emp.salary between salgrade.losal and salgrade.hisal ,emp.dept_id = dept.id

-- 查询研发部的部门id
select id from dept where name = '研发部';-- 根据部门id,查询研发部的员工信息,得到研发部员工子表
select * from emp where dept_id = ( select id from dept where name = '研发部' );-- 再从研发部员工子表中查询信息
select e.*,d.name,s.grade
from ( select * from emp where dept_id = ( select id from dept where name = '研发部' ) ) e 
inner join dept d inner join salgrade s on e.dept_id = d.id and ( e.salary between s.losal and s.hisal );

7.查询 “研发部” 员工的平均工资

解法一:内连接 + 查询条件
表: emp , dept
连接条件 : emp.dept_id = dept.id
查询条件 : dept.name = ‘研发部’

select avg(e.salary) from emp e, dept d where e.dept_id = d.id and d.name = '研发部';

解法二:子查询

-- 查询研发部的部门id
select id from dept where name = '研发部';-- 根据部门id,查询研发部的员工平均薪资
select avg(salary) from emp where dept_id = ( select id from dept where name = '研发部' );

8.查询工资比 “灭绝” 高的员工信息

-- 查询"灭绝"的工资
select salary from emp where name = '灭绝';-- 查询比指定工资高的员工信息
select * from emp where salary > ( select salary from emp where name = '灭绝' );

9.查询比平均薪资高的员工信息

-- 查询员工平均工资
select avg(salary) from emp;-- 查询比指定工资高的员工信息
select * from emp where salary > ( select avg(salary) from emp );

10.查询低于本部门平均工资的员工信息 *

解法一:自连接 + 子查询
表: emp e1,emp e2
连接条件 : e1.dept_id = e2.dept_id

-- 查询指定部门平均薪资
select avg(e1.salary) from emp e1 where e1.dept_id = 1;
select avg(e1.salary) from emp e1 where e1.dept_id = 2;-- 查询低于本部门平均工资的员工信息
select * from emp e2 where e2.salary < ( select avg(e1.salary) from emp e1 where e1.dept_id = e2.dept_id );

解法二:子查询 + 内连接 + 查询条件
子查询: 部门平均工资子表,( select dept_id,avg(salary) avg_sal from emp group by dept_id ) d
表: emp e,d
连接条件 : e.dept_id = d.dept_id
查询条件:e.salary < d.avg_sal

-- 查询各部门的平均工资(以部门分组),得到部门平均工资子表
select dept_id,avg(salary) avg_sal from emp group by dept_id;-- 查询低于本部门平均工资的员工信息
select e.* from emp e inner join ( select dept_id,avg(salary) avg_sal from emp group by dept_id ) d
on e.dept_id = d.dept_id where e.salary < d.avg_sal;

11.查询所有的部门信息, 并统计部门的员工人数 *

解法一:

select d.id, d.name , ( select count(*) from emp e where e.dept_id = d.id ) '人数' from dept d;

解法二:

-- 统计部门的员工人数,得到部门人数子表
select dept_id,count(*) emp_count from emp group by dept_id;-- 使用左连接,加上ifnull(d2.emp_count,0),当dept_id为null时显示为0,否则显示d2.emp_count
select d1.id,d1.name,ifnull(d2.emp_count,0) '人数' from dept d1
left join ( select dept_id,count(*) emp_count from emp group by dept_id ) d2 on d1.id = d2.dept_id;

12.查询所有学生的选课情况, 展示出学生名称, 学号, 课程名称 *

-- 方式一
select s.name,s.no,c.name from student s,course c,student_course sc 
where s.id = sc.studentid and c.id = sc.courseid;-- 方式二
select s.name,s.no,c.name from student s inner join course c inner join student_course sc
on s.id = sc.studentid and c.id = sc.courseid;

总结:

  • 在编写SQL语句时,先 select * from 表名,在编写完from > where > group by > having 这些子句后,然后再看应该把 * 改成哪张表的什么字段。

  • 多表查询时,当题中出现 对应关联及其 等字眼时,要考虑此时是外连接,然后列出表结构和连接条件,如表结构:emp, dept ,连接条件:emp.dept_id = dept.id

  • 在确定了是外连接的情况下,当出现需要查询某张表的所有数据(信息)时,要想到左外连接。

  • 相比内、外、自连接编写的SQL语句,使用子查询的方式编写的SQL语句会更为复杂。

6. 事务

6.1 事务简介

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

就比如: 张三给李四转账1000块钱,张三银行账户的钱减少1000,而李四银行账户的钱要增加1000。 这一组操作就必须在一个事务的范围内,要么都成功,要么都失败。

在这里插入图片描述

正常情况: 转账这个操作, 需要分为以下这么三步来完成 , 三步完成之后, 张三减少1000, 而李四增加1000, 转账成功。

在这里插入图片描述

异常情况: 转账这个操作, 也是分为以下这么三步来完成 , 在执行第三步是报错了, 这样就导致张三减少1000块钱, 而李四的金额没变, 这样就造成了数据的不一致, 就出现问题了。

在这里插入图片描述

为了解决上述的问题,就需要通过数据的事务来完成,我们只需要在业务逻辑执行之前开启事务,执行完毕后提交事务。如果执行过程中报错,则回滚事务,把数据恢复到事务开始之前的状态。

在这里插入图片描述

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

6.2 事务操作

6.2.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);

1.测试正常情况

-- 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 = '李四';

测试完毕之后检查数据的状态, 可以看到数据操作前后是一致的:

在这里插入图片描述

2.测试异常情况

先执行如下语句,恢复为初始数据:

-- 恢复为初始数据
update account set money = '2000' where name in('张三','李四');

执行以下异常SQL语句:

-- 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 = '李四';

测试完毕之后检查数据的状态, 发现数据在操作前后不一致了:

在这里插入图片描述

6.2.2 控制事务

有两种控制事务的方式:

  • 修改事务的提交方式:SET @@autocommit = 0
  • 开启事务:START TRANSACTION 或 BEGIN

1.修改事务的提交方式

-- 查看/设置事务提交方式
SELECT @@autocommit;
SET @@autocommit = 0; -- 1为自动提交,0为手动提交-- 提交事务
COMMIT;-- 回滚事务
ROLLBACK;

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

2.开启事务

-- 开启事务
START TRANSACTION; -- 或BEGIN-- 提交事务
COMMIT;-- 回滚事务
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):事务一旦提交或回滚,它对数据库中的数据的改变就是永久的。

在这里插入图片描述

6.4 并发事务问题

并发处理事务时,可能会引起如下问题:

  • 赃读
  • 不可重复读
  • 幻读

1.赃读:一个事务读到另外一个事务还没有提交的数据。

在这里插入图片描述

比如B读取到了A未提交的数据。

2.不可重复读:一个事务先后读取同一条记录,但两次读取的数据不同,称之为不可重复读。

在这里插入图片描述

事务A两次读取同一条记录,但是读取到的数据却是不一样的。

3.幻读:一个事务按照条件查询数据时,没有对应的数据行,但是在插入数据时,又发现这行数据已经存在,好像出现了 “幻影”。

在这里插入图片描述

6.5 事务隔离级别

为了解决并发事务所引发的问题,在数据库中引入了事务隔离级别。主要有以下几种:

隔离级别脏读不可重复读幻读
Read uncommitted
Read committed×
Repeatable Read(默认)××
Serializable×××

注:
1. 代表会引发该现象,× 代表解决了该现象。
2.事务隔离级别越高,数据越安全,但是性能越低。

-- 查看事务隔离级别
SELECT @@TRANSACTION_ISOLATION;-- 设置事务隔离级别
SET [ SESSION | GLOBAL ] TRANSACTION ISOLATION LEVEL 
{ READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE };
http://www.lryc.cn/news/70140.html

相关文章:

  • RabbitMQ日常使用小结
  • ​​​​​​​博物馆文物馆藏环境空气质量无线监控系统方案
  • 测试理论----Bug的严重程度(Severity)和优先级(Priority)的分类
  • 斯坦福、Nautilus Chain等联合主办的 Hackathon 活动,现已接受报名
  • 00后卷王,把我们这些老油条卷的辞职信都写好了........
  • JavaEE(系列12) -- 常见锁策略
  • 前端nginx接口跨域
  • 【国产虚拟仪器】基于 ZYNQ 的电能质量系统高速数据采集系统设计
  • Java前缀和算法
  • pico 的两个双核相关函数的延时问题
  • Doxygen源码分析: QCString类依赖的qstr系列C函数浅析
  • 华为OD机试之一种字符串压缩表示的解压(Java源码)
  • Microsoft Project Online部署方案
  • 飞浆AI studio人工智能课程学习(3)-在具体场景下优化Prompt
  • 企业工程行业管理系统源码-专业的工程管理软件-提供一站式服务
  • Ehcache 整合Spring 使用页面、对象缓存
  • Spring Cloud中的服务路由与负载均衡
  • rails routes的使用
  • Linux基础内容(21)—— 进程消息队列和信号量
  • STM32实现基于RS485的简单的Modbus协议
  • springboot服务端接口公网远程调试 - 实现HTTP服务监听【端口映射】
  • zabbix监控之javasnmp自定义监控
  • Inertial Explorer处理pospac数据总结
  • tps和qps的区别是什么?怎么理解
  • 【Java系列】深入解析枚举类型
  • 网络原理(五):IP 协议
  • MySQL---空间索引、验证索引、索引特点、索引原理
  • 选择合适的 MQTT 云服务:一文了解 EMQX Cloud Serverless、Dedicated 与 BYOC 版本
  • uvc驱动ioctl分析下
  • 数据库可视化神器,你在用哪一款呢