小学生都能看懂的MySQL教程

文章目录

一、绪论


1.1数据管理
  • 设计时

    程序 = 数据结构 + 算法

  • 运行时

    进程 = 数据 + 指令

1.1.1何为数据?

数据是能够被记录且具有实际含义的已知事实

  • 大数据(big data)

    • 数据量大
    • 变化速度快
    • 数据类型多
  • 元数据(metadata)

    • 数据的数据

    例如 单纯一个20, 不能确定其含义

    但 age:20 price:20 就能表达出其含义

    age price就是所谓的元数据

1.1.2数据操作
  • 存数据(W)
    • 增加/删除数据
    • 修改数据
  • 取数据®
    • 条件查询
    • 聚合查询
  • 其他(O)
    • 安全性约束
    • 完整性约束(保证数据合法、有意义)
      • 实体完整性约束(如学号作为学生的唯一标识,在增加数据时,如果重复则添加失败)
      • 引用完整性约束(如学生表 属于 系部表,学生不能属于了一个不存在的学院,引用了其他表
      • 域完整性约束(取值范围
      • 用户自定义完整性约束(如 age(integer 15-20):)
    • 数据共享
    • 并发控制(如既有读数据的 又有写数据的,如何控制?)
1.1.3数据管理方案(Database Management System)

在这里插入图片描述

文件操作举例

编写bash脚本
echo -n 'input student no:'
read no
grep $no scores.csv
echo -n 'input course no(c[3],python[4],ds[5]:'
read col
grep $no scores.csv | awk -F ',' -v n=$col  '{print $n}' | sed 's/\"//g'
  • 基于文件的数据管理方案的缺点
    • 当文件格式发生变化,要修改应用程序
    • 文件修改可能造成数据不一致,破坏数据正确性
    • 没有索引,数据查找效率低
    • 只能对整个文件进行访问控制,数据安全性差
    • 没有并发控制,多个应用程序同时读写文件可能产生冲突

数据库管理数据举例

select * from scores where no=1340404;
select name,python from scores where id=1340404;
1.1.4文件管理和数据库对比
文件管理数据库
定义管理存储磁盘上数据的存储、访问和管理方式和位置的进程易于存储、访问、管理和更新的有组织的数据集合
数据一致性数据量大、不一致维护数据一致性
结构结构简单结构复杂
数据共享数据共享困难数据共享容易
冗余高冗余低冗余
安全不安全更安全
备份和恢复无备份和恢复过程有备份和恢复
1.2、数据库系统

数据库系统是高效组织管理数据的系统、由数据库数据库管理系统应用程序三部分构成

在这里插入图片描述

  • 应用程序

    提供了用户与数据库系统的交互界面

  • 数据库管理系统

    DBMS是一种通用的系统软件,便于在不同的用户和应用程序之间组织、存储、操作、控制和维护数据库

  • 数据库

    是一组有组织、共享和持久的相关数据

1.2.1DBMS分类
  • 按逻辑数据模型分
    • RDBMS
    • DBMS
  • 按数据库规模分
    • 网络/企业数据库
    • 桌面数据库
  • 按是否收费分
    • 商业收费版
    • 开源免费版
1.3、DBMS的特色
1.3.1数据独立性

UML(Unified Modeling Language) 统一建模语言

  • 数据模型

    • 模型:模型是对研究对象进行抽象的工具,抽象的结果通常是可视化的图形
    • 建模:建模是对研究对象的抽象的过程。抽象就是对事物进行简化、提取特征或共性,实现模型和事物之间的映射或投影等操作

    在这里插入图片描述

数据模型是完成数据抽象的工具,即用来描述数据、数据联系、数据语义以及一致性约束的一套概念工具

  • 数据模型分类

    • 概念模型

      • E-R模型, UML类模型
    • 逻辑模型

      • 层次模型

      在这里插入图片描述

      • 网络模型

      在这里插入图片描述

      • 关系模型

      在这里插入图片描述

      • 对象模型
      • 半结构化模型

    • 物理模型

      • Heap File模型
      • ISAM模型(索引序列访问模型)
1.3.2数据库模式

数据类型的作用:

①存储空间的大小

②数据取值范围或精度

③能够执行的操作

模式(约等于数据类型)和实例

模式 = 数据类型 + 关系 + 约束

create table instructor
(ID varchar(5),
name varchar(20)not null,
dept_name varchar(20),
salar numeric(8,2),
primary key(ID),
foreign key(dept_name) references department);

模式是数据的定义和描述,实例则是模式在某一时刻的具体数据

模式 = 类型 + 约束 + 联系

数据库的三级模式
  • 外模式 (用户模式,不同用户关注的不同局部)
  • 模式
  • 内模式(如存储所用数据结构为红黑树)
数据独立性(两个层次之间)

若下层是黑盒的话,下层的数据变动不会对上层产生影响

内模式的变化不会影响模式,称为物理数据独立性

模式的变化不会影响外模式,称之为逻辑数据独立性

(不能做到绝对的数据独立性,只能实现一部分的数据独立性)

1.3.3数据库语言
  • 编程语言:SQL
  • 数学语言:关系代数
  • 设计语言:E-R图
编程语言:SQL

SQL包括以下三类

  • DDL(Data Definition Language) 用于定义数据库对象(数据库,表,字段)
    • create 增
    • drop 删
    • alter 改
  • DML(Data Manipulation Language 数据操作语言) 对表中的数据进行操作
    • insert 增
    • delete 删
    • update 改
    • select 查 (DQL:Data Query Language)
  • DCL(Data Control Language) 用来创建用户以及控制用户的权限
    • grant 增
    • revoke 删

DTL 事务处理

  • start transaction,begin
  • commit
  • rollback
数学语言:关系代数

在这里插入图片描述

在这里插入图片描述

投影:选出某个字段的列

基本关系代数运算和派生关系代数运算

在这里插入图片描述

设计语言E-R图

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

联系型的三种数量关系

1:1

1:m

m:n(1:n 和 m : 1)

1.3.4事务处理

事务(transaction)

事务是数据库上一系列操作组成的复杂任务,这些操作要么全执行,要么都不执行

事务的特征

  • 原子性 Atomicity

    事务的操作要么全部执行,要么一个也不执行

  • 一致性 Consistency

    事务之前符合完整性约束 事务之后符合完整性约束

    • 完整性约束
      • 实体完整性约束
      • 引用完整性约束
      • 域完整新约束
      • 用户自定义完整性约束
  • 隔离性 Isolation

    一个事务的执行不受其他事务的干扰

  • 持久性 Durability

    事务一旦提交,它对数据库的修改一定全部持久地写到数据库中

二、DDL


2.1表操作-创建&查询

创建数据库(创建triticale数据库)

create database triticale;

查询当前所使用的数据库

select database();

使用数据库

use 数据库;

创建表(在triticale数据库中创建表tb_uesr)

create table [if not exists] tb_user(   
       id int comment '编号' , 
       name varchar(10) comment '姓名',
       age int comment '年龄',
       gender varchar(1) comment '性别'
       ) comment '用户表';

复制已有表

create table emp1 
as
select * from northwind.employees
where department_id = 50;
#若不要数据,只要表结构
where false;

查询表结构

desc 表名;

在这里插入图片描述

查询建表语句

show create table 表名;
2.2数据类型及案例
数值类型大小有符号(SIGNED)范围无符号(UNSIGNED)范围描述
tinyint1B(-128,127)(0,255)小整数值
smallint2B(-32768,32767)(0,65535)大整数值
mediumint3B(-8388608,8388607)(0,16777215)大整数值
int或integer4B(-2147483648,2147483647)(0,4294967295)大整数值
bigint8B(-263,263-1)(0,2^64-1)极大整数值
float4B(-3.402823466 E+38, 3.402823466351 E+38)0和(1.175494351 E-38,3.402823466 E+38)单精度浮点数值
double8B(-1.7976931348623157 E+308,1.7976931348623157 E+308)0和(2.2250738585072014 E-308,1.7976931348623157 E+308)双精度浮点数值
decimal依赖于M(精度)和D(标度)的值依赖于M(精度)和D(标度)的值小数值(精确定点数)
字符串类型大小描述
char0-255定长字符串
varchar0-65535变长字符串
tinyblob0-255不超过255个字符的二进制数据
tinytext0-255超文本字符串
blob0-65535二进制形式的长文本数据
text0-65535长文本数据
mediumblob0-16777215二进制形式的中长文本数据
mediumtext0-16777215中等长度的文本数据
longblob0-4294967295二进制形式的极大文本数据
longtext0-4294967295极大文本数据
日期类型大小范围格式描述
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混合日期和时间值,时间戳

[案例] 根据需求创建表(设计合理的数据类型,长度)

编写一张员工信息表,要求如下

1、编号(纯数字)

2、员工工号(字符串类型,长度不超过10位)

3、员工姓名(字符串类型,长度不超过10位)

4、性别(男/女,存储一个汉字)

5、年龄(正常人年龄,不可能存储负数)

6、身份证号(二代身份证号均为18位,可能带有X字符)

7、入职时间(取值年月日即可)

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.3表操作-修改&删除
  • 添加字段

    alter table 表名 add 字段名 类型(长度) comment注释;
    

    新增字段的位置

    alter table 表名 add 字段名 类型(长度) first/ after 字段名;
    

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

    alter table emp add nickname varchar(20);
    
  • 修改字段名和字段类型

    alter table 表名 modify 字段[类型,约束];
    
    alter table 表名 change 旧字段名 新字段名 类型(长度) comment注释;
    

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

    alter table emp change nickname username varchar(30);
    
  • 删除字段

    alter table 表名 drop 字段名;
    

    将emp表中的username字段删除

    alter table emp drop username;
    
  • 修改表名

    alter table 表名 rename to 新表名;
    

    将emp表的表名修改为employee0

    alter table emp rename to employee;
    
  • 删除表

    drop table[if exists] 表名;
    

    将tb_user表删除

    drop table if exists tb_user;
    

    删除指定表,并重新创建该表

    truncate table 表名;
    

    删除employee表并重新创建该表

    truncate table employee;
    

清空表数据

truncate table 表名;
#truncate 不支持回滚、速度快,操作风险高,不建议使用
delete from 表名;
#支持回滚

三、DML


  • 添加数据(insert)
  • 修改数据(update)
  • 删除数据(delete)
3.1添加数据

在指定字段添加数据

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……);

注意

  • 插入数据时,指定的字段顺序需要与值的顺序是一一对应的
  • 字符串和日期数据应该包含在引号中
  • 插入的数据大小,应该在字段的规定范围内
insert into employee(id, workno, name) values(1, '1', 'Bob');
insert into employee values(2,'2','Alice','女',10,'123456789012345678','2022-01-01');
3.2修改数据
update 表名 set 字段名 = 值1, 字段名2 = 值2,……[where 条件];

将id为1的数据的name改成张三

update employee set name = '张三' where id = 1;

将id为1的数据的name改成李四,gender 改为 男

update employee set name = '李四', gender = '男' where id = 1;

将所有员工的入职时间改成 2008-01-01

update employee set entrydate = '2008-01-01';
3.3删除数据
delete from 表名 [where 条件];

删除gender为女的员工

delete from employee where gender = '女';

删除所有员工

delete from employee;

四、DQL


查询关键字 select

语法

select
	字段列表
from 
	表明列表
where
	条件列表
group by 
	分组字段列表
having
	分组后条件列表
order by
	排序字段列表
limit
	分页参数
4.1基本查询
select 字段1,字段2,字段3…… from 表名;
select * from 表名;

设置别名

select 字段1 as 别名,字段2 as 别名,字段3 as 别名…… from 表名;

去除重复记录

select distinct 字段列表 from 表名;
4.2条件查询
select 字段列表 from 表名 where 条件列表;

条件

比较运算符功能
>大于
>=大于等于
<小于
<=小于等于
=等于
<> 或 !=不等于
between…and…在某个范围之内(含最小,最大值)
in(…)在in之后的列表中的值,多选一
like模糊匹配(_匹配单个字符,%匹配任意个字符)
is null是null
逻辑运算符功能
and 或 &&并且
or 或 ||或者
not 或 !

查询年龄等于18的员工

select * from employee.table_name where age = 18;

在这里插入图片描述

查询年龄小于30的员工

select * from employee.table_name where age < 30;

在这里插入图片描述

查询没有身份证号的员工

select * from employee.table_name where idcard is null;

在这里插入图片描述

查询有身份证号的员工

select * from employee.table_name where idcard is not null;

在这里插入图片描述

查询身份证号带X的员工

select * from employee.table_name where idcard like '%X%';

在这里插入图片描述

查询年龄在16到20岁(包含)之间的员工

select * from employee.table_name where age between 16 and 20;

在这里插入图片描述

查询年龄为女且年龄小于25的员工

select * from employee.table_name where gender = '女' and age < 25;

在这里插入图片描述

查询年龄等于18或20或40的员工

select * from employee.table_name where age in(18,20,40);
select * from employee.table_name where age = 18 || age = 20 || age = 40;

在这里插入图片描述

查询姓名为三个字的员工信息

select * from employee.table_name where name like '___';

在这里插入图片描述

4.3聚合函数

将一列数据作为一个整体,进行纵向计算

函数功能
count统计数量
max最大值
min最小值
avg平均值
sum求和
select 聚合函数(字段列表) from 表名;

统计该企业员工数量

select count(*) from employee.table_name;

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

select avg(age) from employee.table_name;

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

select max(age) from employee.table_name;

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

select min(age) from employee.table_name;

统计上海地区员工年龄之和

select sum(age) from employee.table_name where workaddress = '上海';
4.4分组查询

经常和聚合函数一起使用

关键字 group by

select 字段列表 from 表名 where 条件 group by 分组字段名 having 分组后过滤条件;
  • where与having的区别
    • 执行时机不同:where是分组之前进行过滤,不满足where条件,不参与分组;而having是分组后对结果进行过滤
    • 判断条件不同:where不能对聚合函数进行判断,而having可以

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

select gender,count(*) as gender_count from employee.table_name group by gender;

在这里插入图片描述

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

select gender,avg(age) from employee.table_name group by gender;

在这里插入图片描述

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

select workaddress,count(*) from employee.table_name where age > 18 group by workaddress having count(*) >=3;
4.5排序查询

关键字 order by

select 字段列表 from 表名 order by 字段1 排序方式1,字段2,排序方式2;
  • 排序方式
    • acs 升序(默认)
    • desc降序

根据年龄对员工进行升序排序

select * from employee.table_name order by age asc;

在这里插入图片描述

根据入职时间,对员工进行降序排序

select * from employee.table_name order by entrydate desc;

在这里插入图片描述

根据年龄对公司的员工进行升序排序,年龄相同,再按照入职时间进行降序排序

select * from employee.table_name order by age asc, entrydate desc;

在这里插入图片描述

4.6分页查询
select 字段列表 from 表名 limit 起始索引,查询记录数;

注意

  • 起始索引从0开始,起始索引 = (查询页码 - 1)* 每页显示的记录数
  • 分页查询是数据库方言,不同的数据库有不同的实现,MySQL中是limit
  • 如果查询的是第一页数据,起始索引可以省略,直接简写为limit 10

查询第一页员工数据,每一页展示5条记录

select * from employee.table_name limit 5;

在这里插入图片描述

查询第二页员工数据,每一页展示5条记录

select * from employee.table_name limit 5 5;

在这里插入图片描述

4.7执行顺序

1、from

2、where

3、group by

​ having

4、select

5、order by

6、limit

五、DQL练习


  • 查询年龄为20,21,22,23岁的女性员工信息
select * from employee.table_name where gender = '女' and age in(20,21,22,23);

在这里插入图片描述

  • 查询性别为男,并且年龄在20-40岁以内的(包含)且姓名为三个字的员工
select * from employee.table_name where gender = '男' and age between 20 and 40 and name like '___';

在这里插入图片描述

  • 统计年龄小于六十岁的男性员工和女性员工数量
select gender,count(*) as sum from employee.table_name where age < 60 group by gender;

在这里插入图片描述

  • 查询所有年龄小于等于25岁员工的姓名、年龄和入职时间,并对查询结果按年龄升序排序,如果年龄相同按入职时间降序排序
select name, age,entrydate from employee.table_name where age <= 25 order by age asc, entrydate desc;

在这里插入图片描述

  • 查询性别为男,且年龄在20-40岁(包含)以内的前五个员工信息,对查询的结果按照升序排序,年龄相同按入职时间升序排序
select * from employee.table_name where gender = '男' and age between 20 and 40 order by age asc, entrydate asc limit 5;

在这里插入图片描述

六、DCL


数据控制语言,用来管理数据库用户、控制数据库的访问权限

6.1管理用户

1、查询用户

select user from mysql.user;

2、创建用户

create user '用户名'@'主机名' identified by '密码';

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

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

创建用户itcast,能在任意主机访问,密码为123456

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

3、修改用户密码

alter user '用户名'@'主机名' identified with mysql_native_passwork by '新密码';

修改用户itcast的密码为1234

alter user 'itcast'@'localhost' identified with mysql_native_password by '1234';

4、删除用户

drop user '用户名'@'主机名';

删除itcast@localhost用户

drop user 'itcast'@'localhost';

注意:

  • 主机名可以使用%通配
  • 这类SQL开发人员操作的比较少,主要是DBA(数据库管理员)使用
6.2权限控制
权限说明
all, all privileges所有权限
select查询数据
insert插入数据
update修改数据
delete删除数据
alter修改表
drop删除数据库/表/视图
create创建数据库/表

1、查询权限

show grants for '用户名'@'主机名';

2、查询权限

grant 权限列表 on 数据库名称.表名 to '用户名'@'主机名';

3、撤销权限

revoke 权限列表 on 数据库名称.表名 from '用户名'@'主机名';

七、函数


7.1字符串函数
函数功能
concat(s1,s1,…,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,strat,len)返回字符串str从start位置起的len个长度的字符串

字符串拼接

select concat('Hello','World','!');

lower

select lower('Hello');

upper

select upper('hello');

lpad

select lpad('01',5,'-');

在这里插入图片描述

rpad

select rpad('01',5,'-');

在这里插入图片描述

trim

select trim(' hello world   ');

substring

select substring('hello world',1,5);
# 字符串索引从1开始

【练习】

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

update table_name set workno = lpad(workno,5,0);

在这里插入图片描述

7.2数值函数
函数功能
ceil(x)向上取整
floor(x)向下取整
mod(x,y)返回x/y的模
rand()返回0-1内的随机数
round(x,y)求参数x的四舍五入值,保留y位小数
  • cell
select ceil(1.5);
  • floor
select floor(1.5);
  • mod
select mod(5,4);
  • rand
select rand();
  • round
select round(2.345,2);

【练习】

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

select lpad(round(1000000 * rand(),0),6,0);
7.3日期函数
函数功能
curdate()返回当前日期
curtime()返回当前时间
now()返回当前日期和时间
year()获取指定date的年份
month()获取指定date的月份
day()获取指定date的日期
date_add(date,INTERVAL expr type)返回一个日期/时间值加上一个时间间隔expr后的时间值
datediff(date,date2)返回起始时间date1和结束时间date2之间的天数
  • curdate()
select curdate();
  • curtime()
select curtime();
  • now()
select now();
  • year()
select year(now());
  • month()
select month(now());
  • day()
select day(now());
  • date_add()
select date_add(now(),INTERVAL 70 DAY);
  • datediff()
select datediff('2024-4-13',now());

【练习】

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

select name,datediff(curdate(),entrydate) as work_day from table_name order by work_day desc;
7.4流程控制函数
函数功能
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,…否则返回默认值
case expr when val1 then res1 … else default end如果expr的值等于val1,则返回res1,…否则返回default默认值
  • if
select if(true,'ok','error');
  • ifnull
select ifnull('OK','Default');
  • case

查询员工姓名和工作地址(如果是北京/上海,则是一线城市,其他为二线城市)

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

在这里插入图片描述

【练习】

在scores表中,统计班级各个学员的成绩,展示规则如下:

>=85,展示优秀

>=60,展示及格

否则,展示不及格

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

在这里插入图片描述

八、约束


概念

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

目的

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

分类

约束描述关键字
非空约束限制该字段的数据不能为nullnot null
唯一约束保证该字段的所有数据都是唯一、不重复的unique
主键约束主键是一行数据的唯一标识,要求非空且唯一primary key
默认约束保存数据时,如果未指定该字段的值,则采用默认值default
检查约束保证字段值满足某一个条件check
外键约束用来让两张表的数据之间建立连接,保证数据的一致性和完整性foreign key

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

其中列级约束有六种:主键Primary key、外键foreign key 、唯一 unique、检查 checck 、默认default 、非空/空值 not null/ null

表级约束有四种:主键、外键、唯一、检查

【案例】根据需求,完成表的创建

字段名字段含义字段类型约束条件约束关键字
idID唯一标识int主键,并且自动增长primary key,AUTO_INCREMENT
name姓名varchar(10)不为空,并且唯一not null, nuique
age年龄int大于0,并且小于等于120check
status状态char(1)如果没有指定该值,默认为1default
gender性别char(1)
create table user(
    id int primary key auto_increment comment '主键',
    name varchar(10) not null unique comment '姓名',
    age int check ( age > 0 && age <= 120 ) comment '年龄',
    status char(1) default '1' comment '状态',
    gender char(1) comment  '性别'

) comment '用户表';

验证主键约束

insert into user(name,age,status,gender) values ('Tom1',19,'1','男'),('Tom2',25,'0','男');

在这里插入图片描述

添加约束

alter table 表名 modify 字段名 数据类型 约束;

删除约束

alter table 表名 drop constraint 字段名;
或者
alter table 表名 modify 字段名 数据类型;

验证非空约束

insert into user(name,age,status,gender) values (null,19,'1','男');

在这里插入图片描述

验证唯一约束

insert into user(name,age,status,gender) values ('Tom2',19,'1','男');

在这里插入图片描述

复合的unique约束

alter table 表名 add constraint [唯一值约束的名字] unique(字段名,字段名);
#如果不加名字,那么约束的名字就是列名/第一列的名字
#此时,添加的是表级约束

复合唯一约束可以解决多列组合值唯一性的问题。例如,在一个用户表中,我们可能希望确保每个用户的身份证号码和手机号码的组合值是唯一的。通过创建一个包含身份证号码和手机号码的复合唯一约束,我们可以确保每个用户在这两个列的组合下是唯一的。

删除复合的unique约束

alter table 表名 drop index 约束名;
或者
alter table 表名 drop constraint 约束名;

验证默认约束

insert into user(name,age,gender) values ('Tom3',19,'男');

在这里插入图片描述

8.1外键约束

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

在这里插入图片描述

添加外键

create table 表名(
	字段名 数据类型,
    ...
    [constraint][外键名称] foreign key(外键字段名) references 主表(主表列名)
);

例如

create table city(
id int not null,
name varchar(10),
primary key(id)
);
create table tb_character(
id int,
name varchar(25) not null,
location int,
primary key(id),
foreign key(location) references city(id)
);

查看外键

desc 表名;

select * from
information_schema.table_constraints
where table_name = '表名';
alter table 表名 add constraint 外键名称 foreign key(外键字段名) references 主表(主表列名);
alter table emo add constraint fk_emp_dept_id foreign key(dept_id) references dept(id);

删除外键

alter table 表名 drop foreign key 外键名称;
alter table emp drop foreign key fk_emp_dept_id;

外键的删除/更新行为

行为说明
no action当在父表中删除/更新对应记录时,首先检查该记录是否有对应外键,如果有则不允许删除/更新。(与restrict一致)
restrict当在父表中删除/更新对应记录时,首先检查该记录是否有对应外键,如果有则不允许删除/更新。(与no action一致)
cascade当在父表中删除/更新对应记录时,首先检查该记录是否有对应外键,如果有,则也删除/更新外键在子表中的记录
set null当在父表中删除对应记录时,首先检查该记录是否有对应外键,如果有则设置子表中该外键值为null(这就要求该外键允许取null)
set default父表有变更时,子表将外键列设置成一个默认的值(Innodb不支持)
alter table 表名 add constraint 外键名称 foreign key(外键字段) references 主表名(主表字段名) on update 行为 on delete 行为;

九、多表查询


首先创建两表emp和dept,其中emp的dept_id关联dept表中的id字段

insert into dept(name) values('研发部'),('市场部'),('财务部'),('销售部'),('总经办'),('人事部');
insert into emp(name,age,job,salary,entrydate,managerid,dept_id) values
                                    ('金庸',66,'总裁',20000,'2000-01-01',null,5),
                                    ('张无忌',20,'项目经理',12500,'2005-12-05',1,1),
                                    ('杨逍',33,'开发',8400,'2000-11-03',2,1),
                                    ('韦一笑',48,'开发',11000,'2002-02-05',2,1),
                                    ('常遇春',43,'开发','10500','2004-09-07',3,1),
                                    ('小昭',19,'程序员鼓励师',6600,'2004-10-12',2,1),
                                    ('灭绝',60,'财务总监',8500,'2002-09-12',1,3),
                                    ('周芷若',19,'会计',48000,'2006-06-02',7,3),
                                    ('丁敏君',23,'出纳',5250,'2009-05-13',7,3),
                                    ('赵敏',20,'市场部总监',12500,'2004-10-12',1,2),
                                    ('鹿杖客',56,'职员',3750,'2006-10-03',10,2),
                                    ('鹤笔翁',19,'职员',3750,'2007-05-09',10,2),
                                    ('方东白',19,'职员',5500,'2009-02-12',10,2),
                                    ('张三丰',88,'销售总监',14000,'2004-10-12',1,4),
                                    ('俞莲舟',38,'销售',4600,'2004-10-12',14,4),
                                    ('宋远桥',40,'销售',4600,'2004-10-12',14,4),
                                    ('陈友谅',42,null,2000,'2011-10-12',1,null);
alter table emp add constraint fk_emp_dept_id foreign key(dept_id) references dept(id);

若直接查询俩表,则为俩表的笛卡尔积

select * from emp, dept;

在这里插入图片描述

那么如何消除无效部分呢

select * from emp,dept where emp.dept_id = dept.id;

但因有一员工部门编号为null,因此无法查到该员工

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

内连接查询的是两张表交集的部分

  • 隐式内连接

    select 字段列表 from 表1,表2 where 条件;
    
  • 显式内连接

    select 字段列表 from 表1 join 表2 on 连接条件;
    

查询每一个员工的姓名,以及关联部分的名称

隐式内连接

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

显式内连接

select e.name,d.name from emp as e join dept as d on e.dept_id = d.id;
9.3外连接
  • 左外连接

    select 字段列表 from 表1 left [outer] join 表2 on 条件;
    

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

select e.*, d.name from emp as e left outer join dept as d on e.dept_id = d.id;
  • 右外连接

    select 字段列表 from 表1 right [outer] join 表2 on 条件;
    

查询dept表的所有数据,和对应的员工信息

select d.*, e.* from emp as e right outer join dept as d on e.dept_id = d.id;
9.4自连接
select 字段列表 from 表A as 别名A join 表A as 别名B on 条件;

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

查询员工以及所属领导的名字

select A.name as '员工', B.name as '所属领导' from emp as A join emp as B on A.managerid = B.id;

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

select A.name as '员工', B.name as '所属领导' from emp as A left outer join emp as B on A.managerid = B.id;
9.5联合查询 union

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

select 字段列表 from 表A ...
union [all]
select 字段列表 from 表B ...;

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

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

在这里插入图片描述

可见,鹿杖客出现了两次,若想得到去重后的结果,只需将union all 改为 union

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

在这里插入图片描述

对于联合查询的多张表的列数必须保持一致,字段类型也需要保持一致

9.6子查询

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

select * from t1 where colimn1 = (select colimn1 from t2);

子查询外部的语句可以是insert、update、delete、select的任何一个

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

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

根据子查询位置,分为:where之后、from之后、select之后

9.6.1标量子查询

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

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

查询销售部所有员工信息

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

在这里插入图片描述

查询在’方东白’入职之后的员工信息

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

在这里插入图片描述

9.6.2列子查询

列子查询返回的结果是一列(可以是多行)

常用的操作符in、not in、any、some、all

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

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

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

在这里插入图片描述

查询比财政部所有人工资都高的员工信息

select * from emp where salary > all(select salary from emp where dept_id = (select id from dept where name = '财政部'));

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

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

在这里插入图片描述

9.6.3行子查询

常用操作符:= 、 <> 、 in 、 not in

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

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

在这里插入图片描述

9.6.4表子查询

常用操作符 in

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

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

在这里插入图片描述

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

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

在这里插入图片描述

9.6.5关联子查询

​ 内查询中引用外查询的表中字段

select * from tb_a as a where a.xx > (select max() from th_b as b where b.yy > a.zz);
9.7案例

根据需求完成SQL语句的编写

同时准备一张薪资等级表

create table salgrade(
	grade int,
    losal int,
    hisal int
)comment '薪资等级表';
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);
  • 查询员工的姓名、年龄、职位、部门信息(隐式内连接)
 select e.name, e.age, e.job, d.name from emp as e, dept as d where e.dept_id = d.id;
  • 查询年龄小于30岁的员工的姓名、年龄、职位、部门信息(显式内连接)
select e.name, e.age, e.job, d.name from emp as e inner join dept as d on e.dept_id = d.id where e.age < 30;
  • 查询拥有员工的部门ID、部门名称
select distinct d.id, d.name from emp as e, dept as d where e.dept_id = d.id;
  • 查询所有年龄大于40岁的员工,及其归属的部门名称;如果员工没有分配部门,也需要展示出来
select e.*, d.name from emp as e left join dept as d on e.dept_id = d.id where e.age > 40;
  • 查询所有员工的工资等级
select e.name, s.grade from emp as e, salgrade as s where e.salary <= s.hisal and salary >= s.losal;
  • 查询研发部所有员工的信息及工资等级
select e.*, s.grade from emp as e, dept as d, salgrade as s where (e.salary between s.losal and s.hisal) and e.dept_id = d.id and d.name = '研发部';
  • 查询研发部员工的平均工资
select avg(e.salary) from emp as e, dept as d where e.dept_id = d.id and d.name = '研发部' ;
  • 查询工资比灭绝高的员工信息
select * from emp as e where salary > (select salary from emp where name = '灭绝');
  • 查询比平均薪资高的员工信息
select * from emp where salary > (select avg(salary) from emp);
  • 查询低于本部门平均工资的员工信息
select * from emp as e2 where e2.salary < (select avg(salary) from emp as e1 where e1.dept_id = e2.dept_id);
  • 查询所有的部门信息,并统计部门的员工人数
select id, name ,(select count(*) from emp as e where e.dept_id = d.id) as '人数' from dept as d;

十、事务


10.1事务简介

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

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

10.2事务操作

首先我们准备一张表

create table account(
	id int auto_increment primary key comment '主键ID',
    name varchar(10) comment '姓名',
    money int comment '余额'
)comment '账户表';
insert into account(id, name, money) values(null,'张三',2000),(null,'李四',2000);

转账操作

1、查询张三账户余额

select * from account where name = '张三';

2、将张三账户余额-1000

update account set money = money - 1000 where name = '张三';

3、将李四账户余额+1000

update account set money = monet + 1000 where name = '李四';

方式一(手动关闭自动提交,通过commit提交事务)

查看/设置事务提交方式(1为自动提交,0为手动提交)

select @@autocommit;
set @@autocommit=0;

提交事务

commit;

回滚事务

rollback;

方式二(通过指令显式地开启事务,后面需要有commit和rollback指令)

开启事务

start transaction 或 begin;

提交事务

commit;

回滚事务

rollback;
10.3事务的四大特性
  • 原子性

    事务是不可分割的最小操作单元,要么全部成功,要么全部失败

  • 一致性

    事务完成时,必须使所有的数据都保持一致状态

  • 隔离性

    数据库系统提供的隔离机制,保证事务在不受外部并发操作影响的独立环境下运行

  • 持久性

    事务一旦提交或回滚,它对数据库中的数据的改变就是永久的

10.4并发事务问题
问题描述
脏读一个事务读到另一个事务还没有提交的数据
不可重复读一个事务先后读取同一条记录,但两次读取的数据不同
幻读一个事务按照条件查询数据时,没有对应的数据行,但是在插入数据时,又发现这行数据已经存在,好像出现了“幻影”
10.5事务隔离级别
隔离级别脏读是否会出现不可重复读是否会出现幻读是否会出现
read uncommitted
read committed×
repeatable read(默认)××
serializable×××

查看事务隔离级别

select @@transaction_isolation;

低版本

select @@tx_isolation;

设置事务隔离级别

set [session|global] transaction isolation level {read uncommitted | read committed |repeatable read| serializable}

验证read committed解决脏读问题

在这里插入图片描述

验证read uncommitted无法解决脏读问题

在这里插入图片描述

验证read committed无法解决不可重复读

在这里插入图片描述

验证repeatable read可以解决不可重复读

在这里插入图片描述

验证repeatable read不能解决幻读问题

在这里插入图片描述

验证serializable能解决幻读问题

当左侧事务开启后,右侧事务会一直等待,直到左侧事务提交后才能继续执行

在这里插入图片描述

在这里插入图片描述

注意:事务隔离级别越高,数据越安全,但是性能越低

十一、存储引擎


11.1 MySQL体系结构

在这里插入图片描述

自上而下分别是连接层、服务层、引擎层、存储层

  • 连接层

    最上层是一些客户端和链接服务,主要完成一些类似于连接处理、授权认证、及相关的安全方案。服务器也会为安全接入的每个客户端验证它所具有的操作权限

  • 服务层

    第二层架构主要完成大多数的核心服务功能,如SQL接口,并完成缓存的查询,SQL的分析和优化,部分内置函数的执行。所有跨存储引擎的功能也在这一层实现,如过程、函数等。

  • 引擎层

    存储引擎真正的负责了MySQL中数据的存储和提取,服务器通过API和存储引擎进行通信。不同的存储引擎具有不同的功能,这样我们可以根据自己的需要,来选取合适的存储引擎

  • 存储层

    主要是将数据存储在文件系统之上,并完成与存储引擎的交互

存储引擎就是存储数据、建立索引、更新/查询数据等技术的实现方式。存储引擎是基于表的,而不是基于库的,所以存储引擎也可被称之为表类型。

1、在创建表时,指定存储引擎

create table 表名(
	字段1 字段1类型 [comment 字段1注释],
    ......
    字段n 字段n类型 [comment 字段n注释]
)engine=innodb [comment 表注释];

2、查看当前数据库支持的存储引擎

show engines;

【例】创建表my_myisam,并指定MyISAM存储引擎

create table my_myisam(
	id int,
    name varchar(10)
)engine = MyISAM;
11.2 InnoDB

InnoDB是一种兼顾高可靠性和高性能的通用存储引擎,再MySQL 5.5之后,InnoDB是默认的MySQL存储引擎

【特点】

  • DML操作遵循ACID模型,支持事务
  • 行级锁,提高并发访问性能
  • 支持外键FOREIGN KEY约束,保证数据的完整性和正确性

【文件】

xxx.idd:xxx代表的是表名,innoDB引擎的每张表都会对应这样一个表空间文件,存储该表的表结构(frm、sdi)、数据和索引。

参数:innodb_file_per_table

在这里插入图片描述

11.3 MyISAM

MyISAM是MySQL早期的默认存储引擎

【特点】

  • 不支持事务,不支持外键
  • 支持表锁,不支持行锁
  • 访问速度块

【文件】

xxx.sdi: 存储表结构信息

xxx.MYD:存储数据

xxx.MYI:存储索引

11.4 Memory

Memory引擎的表数据是存储在内存中的,由于受到硬件问题、或断电问题的影响,只能将这些表作为临时表或缓存使用

【特点】

  • 内存存放
  • hash索引(默认)

【文件】

xxx.sdi:存储表结构信息

特点InnoDBMyISAMMemory
存储限制64TB
事务安全支持--
锁机制行锁表锁表锁
B+tree索引支持支持支持
Hash索引--支持
全文索引支持(5.6版本之后)支持-
空间使用N/A
内存使用中等
批量插入速度
支持外键支持--
11.5 存储引擎选择

在选择存储引擎时,应该根据应用系统的特点选择合适的存储引擎。对于复杂的应用系统,还可以根据实际情况选择多种存储引擎进行组合

  • InnoDB:是MySQL的默认存储引擎,支持事务、外键。如果应用对事务的完整性有比较高的要求,在并发条件下要求数据的一致性,数据操作除了插入和查询之外,还包含很多的更新、删除操作,那么InnoDB存储引擎是比较合适的选择
  • MyISAM:如果应用是以读操作和插入操作为主,只有很少的更新和删除操作,并且对事务的完整新、并发性要求不是很高,那么选择这个存储引擎是非常合适的
  • Memory:将所有的数据保存在内存中,访问速度快,通常用于临时表及缓存。Memory的缺陷就是对表的大小有限制,太大的表无法缓存在内存中,而且无法保障数据的安全性

十二、索引


索引(index)是帮助MySQL高效获取数据的数据结构(有序)。在数据之外,数据库系统还维护着满足特定查找算法的数据结构,这些数据结构以某种方式引用(指向)数据,这样就可以在这些数据结构上实现高级查找算法,这种数据结构就是索引

优势劣势
提高数据检索的效率,降低数据库的IO成本索引列也是要占用空的
通过索引对数据进行排序,降低数据排序的成本,降低CPU的消耗索引大大提高了查询效率,同时却也降低更新表的速度,如对表进行insert,update,delete时,效率降低
12.1 索引结构

MySQL的索引是在存储引擎层实现的,不同的存储引擎有不同的结构,主要包含以下几种:

索引结构描述
B+Tree索引最常见的索引类型,大部分引擎都支持B+树索引
Hash索引底层数据结构是用哈希表实现的,只有精确匹配索引列的查询才有效,不支持范围查询
R-tree(空间索引)空间索引是MyISAM引擎的一个特殊索引类型,主要用于地理空间数据类型,通常使用较少
Full-text(全文索引)是一种通过建立倒排索引,快速匹配文档的方式。类似于Lucene,Solr,ES
索引InnoDBMyISAMMemory
B+tree索引支持支持支持
Hash索引不支持不支持不支持
R-tree索引不支持支持不支持
Full-text5.6版本之后支持支持不支持

我们平时所说的索引,如果没有特别指明,都是B+树结构组织的索引

12.2 BTree

在这里插入图片描述

二叉树缺点:顺序插入时,会形成一个链表,查询性能大大降低。大数据量情况下,层级较深,检索速度慢

红黑树是一个自平衡的二叉树

在这里插入图片描述

但红黑树依然是一种二叉树,在大数据量的情况下,层级较深,检索速度慢

而B-Tree

以一棵最大度数为5的b-tree为例(每个节点最多存储4个key,5个指针)

(树的度数指的是一个节点的最大子节点个数)

在这里插入图片描述

B+Tree

以一棵最大度数为4的b+tree为例

在这里插入图片描述

与B-Tree的区别

①所有的数据都会出现在叶子节点

②叶子节点形成一个单向链表

MySQL索引数据结构对经典的B+Tree进行了优化。在原B+Tree的基础上,增加了一个指向相邻叶子节点的链表指针,就形成了带有顺序指针的B+Tree,提高区间访问的性能(循环双链表)

12.3 Hash

哈希索引就是采用一定的hash算法,将键换算成新的hash值,映射到对应的槽位上,然后存储在hash表中

【特点】

  • Hash索引只能用于对等比较(=,in),不支持范围查询(between, >, <, …)

  • 无法利用索引完成排序操作

  • 查询效率高,通常只需要一次检索就可以了,效率通常要高于B+Tree索引

    在MySQL中,支持hash索引的是Memory引擎,而InnoDB中具有自适应hash功能,hash索引是存储引擎根据B+Tree索引在指定条件下自动构建的

12.4 索引分类
分类含义特点关键字
主键索引针对于表中主键创建的索引默认自动创建,只能有一个primary
唯一索引避免同一个表中某数据列中的值重复可以有多个unique
常规索引快速定位特定数据可以有多个
全文索引全文索引查找的是全文中的关键词,而不是比较索引中的值可以有多个fulltext

在InnoDB存储引擎中,根据索引的存储形式,又可以分成以下两种

分类含义特点
聚簇索引(Clustered Index)将数据存储与索引放到一块,索引结构的叶子节点保存了行数据必须有,而且只有一个
二级索引(Secondary Index)将数据与索引分开存储,索引结构的叶子节点关联的是对应的主键可以存在多个

聚簇索引叶子节点下面挂的是一行的数据

聚簇索引就是主键索引,如果没设置主键,MySQL会自动生成一个隐藏的主键,这个主键就是存数据的

可以通过聚集索引直接查到数据,如果通过二级索引,则需要通过二级索引定位数据对应的聚集索引,再通过聚集索引找到数据

聚簇索引的选取规则

  • 如果存在主键,主键索引就是聚集索引
  • 如果不存在主键,将使用第一个唯一索引作为聚集索引
  • 如果表没有主键,或没有合适的唯一索引,则InnoDB会自动生成一个rowid作为隐藏的聚集索引

在这里插入图片描述

12.5 索引语法
  • 创建索引

    create [unique|fulltext] index index_name on table_name (index_col_name,...);
    
  • 查看索引

    show index from table_name;
    
  • 删除索引

    drop index index_name on table_name;
    

【案例】

1、name字段为姓名字段,该字段的值可能重复,为该字段创建索引

2、id字段的值是非空且唯一的,为该字段创建唯一索引

3、为age、job、salary创建联合索引

4、为entrydate建立合适的索引来提升查询效率

create index idx_emp_name on emp(name);
create unique index idx_emp_id on emp(id);
create index idx_emp_age_job_salary on emp(age,job,salary);
create index idx_emp_entrydate on emp(entrydate);
12.6 性能分析
  • SQL执行频率

    MySQL客户端连接成功后,通过show [session|global] status命令可以提供服务器状态信息。通过如下指令,可以查看当前数据库的insert、update、delete、select的访问频次

    show global status like 'Com_______';
    
  • 慢查询日志

    查看慢查询日志是否打开

    show variables like 'slow_query_log';
    

    慢查询日志记录了所有执行时间超过指定参数(long_query_time,单位:秒,默认10秒)的所有SQL语句的日志

    MySQL的慢查询日志默认没有开启,需要在MySQL的配置文件(/etc/mysql/my.cnf)中配置如下信息

    #开启MySQL慢日志查询功能
    slow_query_log=1
    #设置慢日志的时间为2秒,SQL语句执行时间超过2秒,就会视为慢查询,记录慢查询日志
    long_query_time=2
    

    在/var/lib/mysql目录下会有一个lab-slow.log文件

    通过tail -f lab-slow查看这个文件尾部实时输出的内容

  • profile详情

    show profiles 能够在做SQL优化时帮助我们了解时间都耗费到哪里去了。通过have_profiling参数,能够看到当前MySQL是否支持profile操作:

    select @@have_profiling;
    

    默认profiling是关闭的,可以通过set语句在session/global级别开启profiling:

    set profiling = 1;
    

    执行一系列的业务SQL的操作,然后通过如下指令查看指令的执行耗时:

    #查看每一条SQL的耗时基本情况
    show profiles;
    #查看指定query_id的SQL语句各个阶段的耗时情况
    show profile for query query_id;
    #查看指定query_id的SQL语句CPU的使用情况
    show profile cpu for query query_id;
    
  • explain执行计划

    explain或者desc命令获取MySQL如何执行select语句的信息,包括在select语句执行过程中表如何连接和连接的顺序

    语法:

    #直接在select语句之前加上关键字explain/desc
    explain select 字段列表 from 表名 where 条件;
    

    在这里插入图片描述

    • id

      select查询的序列号,表示查询中执行select子句或者是操作表的顺序(id相同,执行顺序从上到下:id不同,值越大,越先执行)

    • select_type

    表示select的类型,常见的取值有simple(简单表,即不使用表连接或者子查询)、primary(主查询,即外层的查询)、union(union中的第二个或者后面的查询语句)、subquery(select/where之后包含了子查询)等

    • type

      表示连接类型,性能由好到差分别是null、system、const、eq_ref、ref、range、index、all

    • possible_key

      显示可能应用在这张表上的索引,一个或多个

    • Key

      实际使用的索引,如果为NULL,则没有使用索引

    • Key_len

      表示索引中使用的字节数,该值为索引字段最大可能长度,并非实际使用长度,在不损失精确性的前提下,长度越短越好

    • rows

      MySQL认为必须要执行查询的行数,在innodb引擎的表中,是一个估计值,可能并不总是准确的

    • filtered

      表示返回结果的行数占需读取行数的百分比,filtered的值越大越好

12.7 索引使用

针对字段创建索引

create index 索引名 on 表名(字段名);

使用原则

  • 最左前缀法则

    如果索引了多列(联合索引),要遵循最左前缀法则。最左前缀法则指的是查询从索引的最左列开始,并且不跳过索引中的列

    如果跳过了某一列,索引将部分失效(后面的字段索引失效)

    在这里插入图片描述

如该索引了三个字段,按顺序分别是age, job, salary

执行以下SQL语句

explain select * from emp where age = 19 and job = '职员' and salary = 5500;

在这里插入图片描述

如果去掉salary的条件

explain select * from emp where age = 19 and job = '职员' ;

在这里插入图片描述

因此可以得到salary字段的索引长度为5

但如果仅通过job和salary查询

explain select * from emp where job = '职员' and salary = 5500;

在这里插入图片描述

因不符合最左前缀法则,则没使用该联合索引

如果仅仅通过age查询

explain select * from emp where age = 19;

在这里插入图片描述

索引长度为5,说明age的索引长度为5

如果通过age,salary查询

explain select * from emp where age = 19 and salary = 5500;

在这里插入图片描述

可以发现索引长度为5,因为跳过了中间字段job,所以job后面的字段索引失效

注意:最左前缀法则指的是最左侧列的索引必须存在,与在SQL语句中的位置无关

explain select * from emp where job = '职员' and age = 19 and salary = 5500;

在这里插入图片描述

  • 范围查询

    范围查找中,如果查找范围大,且需要回表查询,那么可能执行时间会更高

    联合索引中,出现范围查询(>, <),范围查询右侧的索引失效

explain select * from emp where age < 20 and job = '职员' and salary = 5500;

在这里插入图片描述

索引长度为5,说明job和 salary的索引失效

但如果使用的是 <=或者 >=,则不会出现该情况

explain select * from emp where age <= 19 and job = '职员' and salary = 5500;

在这里插入图片描述

  • 索引列运算

    不要在索引上进行运算操作,索引将失效

若对salary进行函数运算,那么该字段的索引还会生效吗

explain select * from emp where substring(salary,3,2) = '00';

在这里插入图片描述

显然索引未生效

  • 字符串不加引号

    字符串类型字段使用时,不加引号,索引将失效

  • 模糊查询

    如果仅仅是尾部模糊匹配,索引不会失效。如果是头部模糊匹配,索引失效

explain select * from emp where name like "张%";

在这里插入图片描述

explain select * from emp where name like "%三丰";

在这里插入图片描述

  • or连接的条件

    用or分割开的条件,如果or连接的两列一侧有索引,一侧没有索引,那么涉及的索引都不会被用到

explain select * from emp where name = '韦一笑' or managerid = 3;

在这里插入图片描述

如果or前后都有索引呢

explain select * from emp where name = '韦一笑' or salary = 8400;

在这里插入图片描述

  • 数据分布影响

    如果MySQL评估使用索引比全表还慢,则不使用索引

如查询B+树第一个叶子节点和之后的所有节点,使用索引不仅可能会有回表还会有从根节点到叶子节点的时间,不如直接查询全表

12.8 SQL提示

SQL提示,是优化数据库的一个重要手段,简单来说,就是在SQL语句中加入一些人为的提示来达到优化操作的目的

use index(使用这个索引)

explain select * from 表名 use index(索引名) where 条件;

ignore index(不适用这个索引)

explain select * from 表名 ignore index(索引名) where 条件;

force index(必须使用这个索引)

explain select * from 表名 force index(索引名) where 条件;
12.7.2覆盖索引

尽量使用覆盖索引(查询使用了索引,并且需要返回的列,在该索引中已经全部能够找到)减少使用select *

对比下面两条SQL语句结果的Extra

explain select * from emp where age = 19 and job = '职员' and salary = 5500;

在这里插入图片描述

explain select emp.age, emp.job, emp.salary from emp where age = 19 and job = '职员' and salary = 5500;

在这里插入图片描述

using index condition:查找使用了索引,但是需要回表查询数据

using where;using index:查找使用了索引,但是需要的数据都在索引列中能找到,所以不需要回表查询数据

12.7.3 前缀索引

当字段类型为字符串(varchar, text等)时,有时候需要索引很长的字符串,这会让索引变得很大,查询时,浪费大量的磁盘IO,影响查询效率。此时可以只将字符串的一部分前缀,建立索引,这样可以大大节约索引空间,从而提高索引效率

语法

create index idx_xxxx on table_name(column(n));

前缀长度

可以根据索引的选择性来决定,而选择性是指不重复的索引值(基数)和数据表的记录总数的比值,索引选择性越高则查询效率越高,唯一索引的选择性是1,这是最好的索引选择性,性能也是最好的

求某个字段的选择性

select count(distinct 字段名) / count(*) from 表名;

求某个字段前缀的选择性

select count(distinct substring(字段名,1,10)) / count(*) from 表名;
12.7.4 单列索引和联合索引

单列索引:即一个索引只包含单个列

联合索引:即一个索引包含了多个列

在业务场景中,如果存在多个查询条件,考虑针对于查询字段建立索引时,建议建立联合索引,而非单列索引

12.8 索引涉及原则

1、针对于数据量较大,且查询比较频繁的表建立索引

2、针对于常作为查询条件(where)、排序(order by)、分组(group by)操作的字段建立索引

3、尽量选择区分度高的列作为索引,尽量建立唯一索引,区分度越高,使用索引的效率越高

4、如果是字符串类型的字段,字段的长度较长,可以针对于字符的特点,建立前缀索引

5、尽量使用联合索引,减少单列索引,查询时,联合索引很多时候可以覆盖索引,节省存储空间,避免回表,提高查询效率

6、要控制索引的数量,索引并不是多多益善,索引越多,维护索引结构的代价也就越大,会影响增删改的效率

7、如果索引列不能存储NULL值,请在创建表时使用NOT NULL 约束它。当优化器知道每列是否包含NULL值时,它可以更好地确定哪个索引最有效地用于查询

十三、SQL优化


13.1 insert优化
  • 批量操作
insert into tb_test values(1,'Tom'),(2,'Cat'),(3,'Jerry');
  • 手动提交事务
start transaction;
insert into tb_test values(1,'Tom'),(2,'Cat'),(3,'Jerry');
commit;
  • 主键顺序插入
主键乱序插入: 8 1 9 21 88 2 4 15 89 5 7 3
主键顺序插入: 1 2 3 4 5 6 7 8 9 15 21 88 89

大批量插入数据

如果一次性需要插入大批量数据,使用insert语句插入性能较低,此时可以使用MySQL数据库提供的load指令进行插入

#客户端连接服务端时,加上参数 --local-infile
mysql --local-infile -u root -p
#设置全局参数local_infile为1,开启从本地加载文件导入数据的开关
set global local_infile = 1;
#执行load指令将准备好的数据,加载到表结构中
load data local infile '/root/sql1.log' into table 'tb_user' fields terminated by ','lines terminated by '\n';
13.2 主键优化

数据组织方式

在InnoDB存储引擎中,表数据都是根据主键顺序组织存放的,这种存放方式的表称为索引组织表(IOT)

  • 页分裂

页可以为空,也可以填充一半,也可以填充100%。每个页包含了2-N行数据(如果一行数据过大,会行溢出),根据主键排列

若在以下两个页中插入50

在这里插入图片描述

会从第一页的50%位置分裂到第三个页中,然后修改指针

在这里插入图片描述

  • 页合并

当删除一行记录时,实际上记录并没有被物理删除,只是记录被标记(flaged) 为删除并且它的空间变得允许被其他记录声明使用

当页中删除的记录达到MERGE_THRESHOLD(默认为页的50%),InnoDB会开始寻找最靠近的页(前或后)看看是否可以将两个页合并以优化空间使用

在这里插入图片描述

在这里插入图片描述

  • 主键设计原则

    满足业务需求的情况下,尽量降低主键的长度

    插入数据时,尽量选择顺序插入,选择使用AUTO_INCREMENT自增主键

    尽量不使用UUID做主键或者是其他自然主键,如身份证号

    业务操作时,避免对主键的修改

13.3 order by优化

①Using filesort:通过表的索引或全表扫描,读取满足条件的数据行,然后在排序缓冲区sort buffer中完成排序操作,所有不是通过索引直接返回排序结果的排序都叫FileSort排序

②Using index: 通过有序索引顺序扫描直接返回有序数据,这种情况即为using index,不需要额外排序,操作效率高

  • 根据排序字段建立合适的索引,多字段排序时,也遵循最左前缀法则
  • 尽量使用覆盖索引
  • 多字段排序,一个升序一个降序,此时需要注意联合索引在创建时的规则(asc/desc)
create index index_emp_name_salary_ad on emp(name asc, salary desc);
  • 如过不可避免地出现filesort,大数据量排序时,可以适当增大缓冲区大小sort_buffer_size(默认256K)
13.4 group by优化
  • 在分组操作时,可以通过索引来提高效率
  • 分组操作时,索引的使用也是满足最左前缀法则的
13.5 limit优化

一个常见的问题就是limit 2000000,10 此时需要MySQL排序前2000010记录,仅仅返回2000000-2000010的记录,其他记录丢弃,查询排序的代价非常大

select * from tb_sku limit 9000000,10;

可以通过覆盖索引和多表查询的方式优化,为什么不用子查询,因为MySQL可能一些版本不支持该种语法

select s.* from tb_sku as s, (select id from tb_sku order by id limit 9000000,10) as a where s.id = a.id;
13.6 count优化

MyISAM引擎把一个表的总行数存在了磁盘上,因此count(*)的时候会直接返回这个数,效率很高

InnoDB引擎执行count(*)的时候,需要把数据一行一行地从引擎里面读出来,然后累积计数

优化思路:自己计数

count的几种用法

  • count(主键)

    InnoDB引擎会遍历整张表,把每一行的主键id值都取出来,返回给服务层。服务层拿到主键后,直接按行进行累加(主键不可能为null)

  • count(字段)

    没有not null约束:InnoDB引擎会遍历整张表把每一行的字段值都取出来,返回给服务层,服务层判断是否为null,不为null,计数累加。

    有not null约束:InnoDB引擎会遍历整张表把每一行的字段值都取出来,返回给服务层,直接按行进行累加

  • count(1)

    InnoDB引擎遍历整张表,但不取值。服务层对于返回的每一行,放一个数字“1”进去,直接按行进行累加

  • count(*)

    InnoDB引擎并不会把全部字段取出来,而是专门做了优化,不取值,服务层直接按行进行累加

按照效率排序的话,count(字段) < count(主键 id) < count(1) ≈ count(*),所以尽量使用count(*)

13.7 update优化

update更新数据时,如果where后面字段有索引,则是行锁,如果where后面字段没有索引,则会为表锁

十四、视图

视图是一种虚拟存在的表,视图中的数据并不在数据库中实际存在,行和列数据来自自定义视图的查询中使用的表,并且是在使用视图时动态生成的。

也就是说,视图只保存了查询的SQL逻辑,不保存查询结果。所以我们在创建视图的时候,主要的工作就落在创建这条SQL查询语句上。

创建视图

create [or replace] view 视图名称[(列名列表)] as select语句 [with[cascaded|local]check option]
create or replace view stu_v_1 as select  e.name, e.job
    from emp as e
    where e.age <= 33;

查询视图

查询创建视图语句

show create view 视图名称;

查询视图数据

select * from 视图名称;

修改视图

#方式1:
create [or replace] view 视图名称[表名列表] as select语句[with[cascaded|local]check option];
#方式2:
alter view 视图名称[表名列表] as select 语句[with[cascaded|local]check option];

删除视图

drop view [if exists] 视图名称;
14.1 检查选项
create or replace view stu_v_1 as select  e.name, e.job
    from emp as e
    where e.age <= 33;

如果我们用该建表语句创建视图,如果插入一条数据(‘张三’, ‘测试’, 50),那么在该视图中将不会显示,因为创建视图的时候查询条件为 age <= 30,为避免此情况的发生,只需在建表语句后面加上with cascaded check option

!在这里插入图片描述

  • 视图的检查选项

当使用with check option子句创建视图时,MySQL会通过视图检查正在更改的每个行,例如插入,更新,删除,以使其符合视图的定义。MySQL允许基于另一个视图创建视图,它还会检查依赖视图中的规则以保持一致性。为了确定检查的范围,mysql提供了两个选项:

cascaded和local,默认值为cascaded

  • with cascaded check option:级联,不仅要检查当前的视图,还要检查所依赖的视图(所依赖的视图也相当于加上了cascaded),向上兼容
  • with local check option:检查当前视图,检查所依赖的视图,如果所依赖的视图有检查选项则检查,如果没有则不检查
14.2 视图的更新

所依赖的表更新后,视图也会更新

要使视图可更新,视图中的行与基础表中的行之间必须存在一对一的关系,如果视图包含以下任何一项,则视图不可更新

1、聚合函数或窗口函数(sum()、min()、max()、count()等)

2、distinct

3、group by

4、having

6、union或union all

14.3 视图的作用
  • 简单

    视图不仅可以简化用户对数据的理解,也可以简化他们的操作。那些经常使用的查询可以被定义为视图,从而使得用户不必为以后的操作每次指定全部的条件

  • 安全

    数据库可以授权,但不能授权到数据库特定行和特定列上。通常视图用户只能查询和修改他们所能见到的数据。

  • 数据独立

    视图可帮助用户屏蔽真实表结构变化带来的影响

十五、存储过程

存储过程是事先经过编译并存储在数据库中的一段SQL语句的集合,调用存储过程可以简化应用开发人员的很多工作,减少数据在数据库和应用服务器之间的传输,对于提高数据处理的效率是有好处的

存储过程思想上很简单,就是数据库SQL语言层面的代码封装与重用

特点

  • 封装、复用
  • 可以接收参数,也可以返回数据
  • 减少网络交互,提升效率
15.1 基本语法

创建

create procedure 存储过程名称([参数列表])
begin
    --SQL语句
end;

调用

call 名称([参数])

在命令行中,执行创建存储过程的SQL时,需要通过关键字delimiter指定SQL语句的结束符

delimiter %
create procedure e1()
begin
	select count(*) from employees;
end%
delimiter ;
call p1();

查看存储过程

#查询指定数据库的存储过程及状态信息
select * from INFORMATION_SCHEMA.ROUTINES
where ROUTINE_SCHEMA = 'xxx';
#查询某个存储过程的定义
show create procedure 存储过程名称

删除存储过程

drop procedure [if exists] 存储过程名称;
15.2 系统变量

系统变量是MySQL服务器提供,不是用户定义的,属于服务器层面。分别为全局变量(global)、会话变量(session)

查看系统变量

#查看所有系统变量
show [session|global] variables;
#可以通过like模糊匹配方式查找变量
show [session|global] variables like '...';
#可以指定变量的值
select @@[session|global].系统变量名;

设置系统变量

set [session|global] 系统变量名 = 值;
set @@[session|global]系统变量名 = 值;

注意:如果没有指定session|global,默认是session,会话变量

mysql服务器重新启动之后,所设置的全局参数会失效,要想不失效,可以在/etc/my.cnf中配置

15.3 用户自定义变量

用户自定义变量是用户根据需要自己定义的变量,用户变量不用提前声明,在用的时候直接用“@变量名”使用就可以。其作用域为当前连接

赋值

set @var_name = expr [,@var_name = expr] ...;
set @var_name := expr [,@var_name = expr] ...; #建议使用该种运算符,因为在MySQL中比较运算符也是=
select @var_name = expr [,@var_name = expr] ...;
select 字段名 into @var_name from 表名;

例如

select count(*) into @mycount from employees;

使用

select @var_name;

注意:用户定义的变量无需对其声明或初始化,只不过获取到的值为NULL

15.4 局部变量

局部变量是根据需要定义的在局部生效的变量,访问之前,需要declare声明,可用作存储过程内的局部变量和输入参数,局部变量的范围是在其声明的begin…end块

声明

declare 变量名 变量类型 [default ...];

变量类型就是数据库字段类型,int,bigint,char,varchar,date,time等

赋值

set 变量名 = 值;
set 变量名 := 值;
select 字段名 into 变量名 from 表名;

例如

delimiter %
create procedure p2()
begin
	declare emp_count int default 0;
	select count(*) into emp_count from employees;
	select emp_count;
end%
delimiter ;
15.5 if判断
if 条件1 then
elseif 条件2 then
else
end if;

【案例】根据定义的分数score变量,判定当前分数的等级

1、score >= 85分,等级为优秀

2、score >= 60分 且 score < 85分,等级为及格

3、score < 60分,等级为不及格

delimiter %
create procedure p3()
begin 
	declare score int default 58;
	declare result varchar(10);
	if score >= 85 then 
	set result := '优秀';
	elseif score >= 60 then
	set result := '及格';
	else 
	set result := '不及格';
	end if;
	select result;
end %
delimiter ;
15.6 参数
类型含义备注
in该类参数作为输入,也就是需要调用时传入值默认
out该类参数作为输出,也就是该参数可以作为返回值
inout既可以作为输入参数,也可以作为输出参数
delimiter %
create procedure 存储过程名称(in/out/inout 参数名 参数类型)
begin
	SQL语句
end %
delimiter ;

【案例】根据传入的参数score,判定当前分数的等级,并返回

1、score >= 85分,等级为优秀

2、score >= 60分 且 score < 85分,等级为及格

3、score < 60分,等级为不及格

delimiter %
create procedure p4(in score int,out result varchar(10))
begin 
	if score >= 85 then 
	set result := '优秀';
	elseif score >= 60 then
	set result := '及格';
	else 
	set result := '不及格';
	end if;
end %
delimiter ;
call p4(60,@result);
select @result;

【案例】将传入的200分制的分数,进行换算,换算成百分制,然后返回

delimiter %
create procedure p5(inout score double)
begin
	set score := score * 0.5;
end %
delimiter ;
set @score := 124;
call p5(@score);
select @score;
15.7 case

语法一

case case_value
	when when_value1 then statement_list1
	when when_value2 then statement_list2
	...
	else statement_list
end case;

语法二

case
	when search_condition1 then statement_list1
	when search_condition2 then statement_list2 ...
	else statement_list
end case;

【案例】根据传入的月份,判断月份所属的季节(要求采用case结构)

1、1-3月份为第一季度

2、4-6月份为第二季度

3、7-9月份为第三季度

4、10-12月份为第四季度

delimiter %
create procedure p6(in month int)
begin 
	declare result varchar(10);
	case 
	when month >= 1 and month <= 3 then
		set result := '第一季度';
	when month >= 4 and month <= 6 then
		set result := '第二季度';
	when month >= 7 and month <= 9 then
		set result := '第三季度';
	when month >= 9 and month <= 12 then
		set result := '第四季度';
	else 
		set result := '非法参数';
	end case;
	select concat('您输入的月份为:',month,' 所属的季度为:',result);
end %
delimiter ;
15.8 while
while 条件 do
	SQL逻辑
end while;

【需求】计算从1累加到n的值,n为传入的参数值

delimiter %
create procedure p8(in n int)
begin
	declare total int default 0;
	while n>0 do
		set total := total + n;
		set n := n -1 ;
	end while;
	select total;
end %
delimiter ;
15.9 repeat

repeat是有条件的控制循环语句,当满足条件的时候退出循环。具体语法为:

repeat
	SQL逻辑
	until 条件
end repeat;

【需求】计算从1累加到n的值,n为传入的参数值

delimiter //
create procedure p8(in n int)
begin
declare total int default 0;
	repeat
		set total := total + n;
		set n := n - 1;
	until n <= 0
	end repeat;
	select total;
end //
delimiter ;
15.10 loop

loop实现简单的循环,如果不在SQL逻辑中增加退出循环的条件,可以用其来实现简单的死循环。loop可以配合以下两个语句使用:

  • leave:配合循环使用,退出循环
  • iterate:必须用在循环中,作用是跳过当前循环剩下的语句,直接进入下一次循环。
[begin_label:]loop
	SQL逻辑
end loop[end_label];
leave lable; #退出指定标记的循环体
iterate lable; #直接进入下一次循环

【需求】计算从1累加到n的值,n为传入的参数值

delimiter //
create procedure p9(in n int)
begin 
	declare total int default 0;
	sum:loop
		if n <= 0 then 
			leave sum;
		end if;
		set total := total + n;
		set n := n - 1;
	end loop sum;
	select total;
end //
delimiter ;

【需求】计算从1到n之间的偶数累加的值,n为传入的参数值

delimiter //
create procedure p10(in n int)
begin 
	declare total int default 0;
	sum:loop
		if n <= 0 then 
			leave sum;
		end if;
		if n % 2 = 1 then
			set n := n - 1;
			iterate sum;
		end if;
		set total := total + n;
		set n := n - 1;
	end loop sum;
	select total;
end //
delimiter ;
15.11 cursor

游标是用来存储查询结果集的数据类型,在存储过程和函数中可以使用游标对结果集进行循环的处理。游标的使用包括游标的声明、open、fetch和close,其语法如下

声明游标

declare 游标名称 cursor for 查询语句;

打开游标

open 游标名称;

获取游标记录

fetch 游标名称 into 变量[,变量];

关闭游标

close 游标名称;

【案例】根据传入的参数uage,来查询员工表emp中,所有的用户年龄小于等于uage的用户姓名(name)和职位(job),并将用户的姓名和职位插入到所创建的一张新表(id,name,job)中

create procedure p11(in uage int)
begin
    declare u_name varchar(100);
    declare u_job varchar(100);
    #局部变量必须的声明必须放在游标的前面
    declare u_corsor cursor for
    select e.name, e.job
    from emp as e
    where e.age <= uage;
    declare exit handler for sqlstate '02000' close u_corsor;
    drop table if exists tb_user_pro;
    create table if not exists tb_user_pro(
        id int primary key auto_increment,
        name varchar(100),
        job varchar(100)
    );
    open u_corsor;
    while true do
        fetch u_corsor into u_name,u_job;
        insert into tb_user_pro values (null,u_name,u_job);
    end while;
    close u_corsor;
end;

条件处理程序(Handler)

可以用来定义在流程控制结构执行过程中遇到问题时相应的处理步骤。具体语法为:

declare handler_action handler for condition_value [,condition_value] ... statement;

handler_action
	continue:继续执行当前程序
	exit:终止执行当前程序
condition_value
	sqlstate sqlstate_value:状态码,如02000
	sqlwarning:所有以01开头的sqlstate代码的简写
	not found:所有以02开头的sqlstate代码的简写
	sqlexception:所有没有被sqlwarning和not found捕获的sqlstate代码的简写
	

十六、存储函数

存储函数是有返回值的存储过程,存储函数的参数只能是in类型的。具体语法如下

create function 存储函数名称[参数列表]
returns type [characteristic...]
begin
	SQL语句
	return...;
end;

#characteristic说明
#deterministic:相同的输入参数总是产生相同的结果
#no sql:不包含sql语句
#reads sql adta:包含读取数据的语句,包不包含写入数据的语句

【需求】计算从1累加到n的值,n为传入的参数值

delimiter %
create function fun_sum(n int)
returns int deterministic
begin
	declare total int default 0;
	while n > 0 do
		set total := total + n;
		set n := n - 1;
	end while;
	return total;
end %
delimiter ;
select fun_sum(100);

十七、触发器

触发器是与表有关的数据库对象,指在insert/update/delete之前或之后,触发并执行触发器中定义的SQL语句集合。触发器的这种特性可以协助应用在数据库端确保数据的完整性,日志变化,数据校验等操作。
使用别名old和new来引用触发器中发生变化的记录内容,这与其他的数据库是相似的。现在触发器还只支持行级触发,不支持语句级触发

触发器类型new和old
insert型触发器new表示将要或已经新增的数据
update型触发器old表示修改之前的数据,new表示将要或已经修改后的数据
delete型触发器old表示将要或者已经删除的数据

如何创建触发器

delimiter %
create trigger 触发器名字
before/after insert/update/delete
on 表名 for each row #行级触发器
begin 
	trigger_stmt;
end %
delimiter ;

查看

show triggers;

删除

drop trigger [schema_name.]trigger_name; #如果没有指定表名,默认为当前数据库

通过触发器记录emp表数据变更日志,将变更日志插入到日志表emp_logs中,包括增加,修改,删除;

create table emp_logs(
	id int not null auto_increment,
    operation varchar(20) not null comment '操作类型,insert/updare/delete',
    operate_time datetime not null comment '操作时间',
    operate_id int not null comment '操作的ID',
    operate_params varchar(500) comment '操作的参数',
    primary key(id)
)engine=innodb default charset=utf8;
  • insert类型
create trigger emp_insert_trigger
	after insert on emp for each row
begin
	insert into emp_logs(id,operation,operate_time,operate_id,operate_params) values(null,'insert',now(),new.id,concat('插入的数据内容为:id=',new.id,'name=',new.name,'age=',new.age,'job=',new.job,'salary=',new.salary,'entrydate=',new.entrydate,'managerid=',new.managerid,'dept_id=',new.dept_id));
end;
  • update
create trigger emp_update_trigger
	after update on emp for each row
begin
	insert into emp_logs(id,operation,operate_time,operate_id,operate_params) values(null,'update',now(),new.id,concat('更新之前的数据:id=',old.id,'name=',old.name,'age=',old.age,'job=',old.job,'salary=',old.salary,'entrydate=',old.entrydate,'managerid=',old.managerid,'dept_id=',old.dept_id,'|更新之后的数据:id=',new.id,'name=',new.name,'age=',new.age,'job=',new.job,'salary=',new.salary,'entrydate=',new.entrydate,'managerid=',new.managerid,'dept_id=',new.dept_id));
end;
  • delete
create trigger emp_delete_trigger
	after delete on emp for each row
begin
	insert into emp_logs(id,operation,operate_time,operate_id,operate_params) values(null,'delete',now(),old.id,concat('删除之前的数据:id=',old.id,'name=',old.name,'age=',old.age,'job=',old.job,'salary=',old.salary,'entrydate=',old.entrydate,'managerid=',old.managerid,'dept_id=',old.dept_id));
end;

十八、锁


18.1 锁的介绍

锁是计算机协调多个进程或线程并发访问某一资源的机制。在数据库中,除传统的计算机资源(CPU、RAM、I/O)的争用外,数据也是一种供许多用户共享的资源。如何保证数据并发访问的一致性、有效性是所有数据库必须解决的一个问题,锁冲突也是影响数据库并发访问性能的一个重要因素。从这个角度来说,锁对数据库而言显得尤为重要,也更加复杂。

18.2 锁的分类

按照锁的粒度来分,分为以下三类

  • 全局锁:锁定数据库中的所有表
  • 表级锁:每次操作锁住整张表
  • 行级锁:每次操作锁住对应的行数据
18.3 全局锁

全局锁就是对整个数据库实例加锁,加锁后整个实例就处于只读状态,后续的DML的写语句,DDL语句,已经更新操作的事务提交语句都将被阻塞

其典型的使用场景是做全库的逻辑备份,将所有的表进行锁定,从而获取一致性视图,保证数据的完整性

为当前数据库加全局锁

flush tables with read lock;

备份

mysqldump -uroot -p1234 demo > demo.sql

其中root和1234为用户名和密码

demo为数据库

解锁为

unlock tables;

验证操作

先为join_demo数据库加上全局锁

在这里插入图片描述

验证查询和更新操作

在这里插入图片描述

在这里插入图片描述

在linux命令行中将其备份

在这里插入图片描述

在这里插入图片描述

  • 特点

数据库中加全局锁,是一个比较重的操作,存在以下问题

1、如果在主库上备份,那么在备份期间都不能执行更新,业务基本上就得停摆

2、如果从库上备份,那么备份期间从库不能执行主库同步过来的二进制日志(binlog),会导致主从延迟

在InnoDB引擎中,我们可以在备份时加上参数 --single-transaction参数来完成不加锁的一致性数据备份

mysqldump --single-transaction -uroot -p1234 demo > demo.sql
18.4 表级锁

表级锁,每次操作锁住整张表。锁定粒度大,发生锁冲突的概率最高,并发度最低,应用在MyISAM、InnoDB、BDB等存储引擎中

表级锁分成三类

  • 表锁
  • 元数据锁(meta data lock,MDL)
  • 意向锁

表锁

对于表锁,分成两类

1、表共享读锁(read lock)

2、表独占写锁(write lock)

语法

1、加锁: lock tables 表名… read/write

2、释放锁: unlock tables / 客户端断开连接

在这里插入图片描述

元数据锁

MDL加锁过程是系统自动控制,无需显式使用,在访问一张表的时候会自动加上。MDL锁主要作用是维护表元数据的一致性,在表上有活动事务的时候,不可以对元数据进行写入操作

在MySQL5.5中引入了MDL,当对一张表进行增删改查的时候,加MDL读锁(共享);当对表结构进行变更操作的时候,加MDL写锁(排他)

对应SQL锁类型说明
lock tables xxx read/writeSHARED_READ_ONLY/SHARED_NO_READ_WRITE
select、select … lock in share modeSHARED_READ与SHARED_READ、SHARED_WRITE兼容,与EXCLUSIVE互斥
insert、update、delete、select…for updateSHARED_WRITE与SHARED_READ、SHARED_WRITE兼容,与EXCLUSIVE互斥
alter table …EXCLUSIVE与其他的MDL都互斥na’ge

意向锁

为了避免DML在执行时,加的行锁与表锁的冲突,在InnoDB中引入了意向锁,使得表锁不用检查每行数据是否加锁,使用意向锁来减少表锁的检查

1、意向共享锁(IS):由语句 select … lock in share mode添加,与表锁共享锁(read)兼容,与表锁排他锁(write)互斥

2、意向排他锁(IX):由insert、update、delete、select … for updata添加,与表锁共享锁(read)以及排他锁(write)都互斥。意向锁之间不会互斥

通过以下SQL,查看意向锁及行锁的加锁情况

select object_schema,object_name,index_name,lock_type,lock_mode,lock_data from performance_schema.data_locks;
18.5 行级锁

行级锁,每次操作锁住对应的数据。锁定粒度最小,发生冲突的概率最低,并发度最高。应用在InnoDB存储引擎中

InnoDB的数据是基于索引组织的,行锁是通过对索引上的索引项加锁来实现的,而不是对记录加的锁。对于行级锁,主要分为以下三类

1、行锁(Record Lock):锁定单个行记录的锁,防止其他事务对此行进行update和delete。在RC、RR隔离级别下都支持

2、间隙锁(Gap Lock):锁定索引记录间隙(不包含该记录),确保索引记录间隙不变,防止其他事务在这个间隙进行insert,产生幻读。在RR隔离级别下都支持

在这里插入图片描述

3、临键锁(Next-Key Lock):行锁和间隙锁组合,同时锁住数据,并锁住数据前面的间隙Gap。在RR隔离级别下支持

InnoDB实现了以下两种类型的行锁

1、共享锁(S) :允许一个事务去读一行,阻止其他事务获取相同数据集的排他锁

2、排他锁(X):允许获取排他锁的事务更新数据,阻止其他事务获得相同数据集的共享锁和排他锁

在这里插入图片描述

SQL行锁类型说明
insert排他锁自动加锁
update排他锁自动加锁
delete排他锁自动加锁
select不加任何锁
select … lock in share mode共享锁需要手动在select之后加lock in share mode
select … for update排他锁需要手动在select之后加for update

InnoDB的行锁是针对索引加的锁,不通过索引条件检索数据,那么InnoDB将对表中的所有记录加锁,此时就会升级为表锁

默认情况下,InnoDB和REPEATABLE READ 事务隔离级别运行,InnoDB使用next-key锁进行搜索和索引扫描,以防止幻读

1、索引上的等值查询(唯一索引),给不存在的记录加锁时,优化为间隙锁

2、索引上的等值查询(普通索引),向右遍历时最后一个值不满足查询需求时,next-key lock退化为间隙锁

3、索引上的范围查询(唯一索引)–会访问到不满足条件的第一个值为止

注意:间隙锁唯一目的是防止其他事务插入间隙。间隙锁可以共存,一个事务采用的间隙锁不会阻止另一个事务在同一间隙上采用间隙锁

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值