2021-03-04

Mysql

*一、数据库简介*

*1、********数据库的好处*

​ 1.持久化数据到本地

​ 2.可以实现结构化查询,方便管理

*2、********数据库相关概念*

​ 1、DB:数据库,保存一组有组织的数据的容器

​ 2、DBMS:数据库管理系统,又称为数据库软件(产品),用于管理DB中的数据

​ 3、SQL:结构化查询语言,用于和DBMS通信的语言

*3、********数据库存储数据的特点*

​ 1、将数据放到表中,表再放到库中

​ 2、一个数据库中可以有多个表,每个表都有一个的名字,用来标识自己。表名具有唯一性。

​ 3、表具有一些特性,这些特性定义了数据在表中如何存储,类似java中 “类”的设计。

​ 4、表由列组成,我们也称为字段。所有表都是由一个或多个列组成的,每一列类似java 中的”属性”

​ 5、表中的数据是按行存储的,每一行类似于java中的“对象”。

*二、数据库的基础操作*

*1、********MySQL服务的启动和停止*

​ 方式一:计算机——右击管理——服务

​ 方式二:通过管理员身份运行

​ net start 服务名(启动服务)

​ net stop 服务名(停止服务)

*2、********MySQL服务的登录和退出*

​ 方式一:通过mysql自带的客户端

​ 只限于root用户

​ 方式二:通过windows自带的客户端

​ 登录:

​ mysql 【-h主机名 -P端口号 】-u用户名 -p密码

​ 退出:

​ exit或ctrl+C

*3、********MySQL的常见命令*

​ 1、查看当前所有的数据库

​ show databases;

​ 2.打开指定的库

​ use 库名

​ 3.查看当前库的所有表

​ show tables;

​ 4.查看其它库的所有表

​ show tables from 库名;

​ 5.创建表

​ create table 表名(

​ 列名 列类型,

​ 列名 列类型,

​ 。。。

​ );

​ 6.查看表结构

​ desc 表名;

​ 7.查看服务器的版本

​ 方式一:登录到mysql服务端

​ select version();

​ 方式二:没有登录到mysql服务端

​ mysql --version

​ 或

​ mysql --V

*4、********MySQL的语法规范*

​ 1.不区分大小写,但建议关键字大写,表名、列名小写

​ 2.每条命令最好用分号结尾

​ 3.每条命令根据需要,可以进行缩进 或换行

​ 4.注释

​ 单行注释:#注释文字

​ 单行注释:-- 注释文字

​ 多行注释:/* 注释文字 */

*5、********SQL的语言分类*

​ DQL(Data Query Language):数据查询语言

​ select

​ DML(Data Manipulate Language):数据操作语言

​ insert 、update、delete

​ DDL(Data Define Languge):数据定义语言

​ create、drop、alter

​ TCL(Transaction Control Language):事务控制语言

​ commit、rollback

*6、********SQL的常见命令*

​ show databases; 查看所有的数据库

​ use 库名; 打开指定 的库

​ show tables ; 显示库中的所有表

​ show tables from 库名;显示指定库中的所有表

​ create table 表名(

​ 字段名 字段类型,

​ 字段名 字段类型

​ ); 创建表

​ desc 表名; 查看指定表的结构

​ select * from 表名;显示表中的所有数据

*7、********DQL语言的学习*

*7.********1********、********基础查询*

​ 语法:

​ SELECT 要查询的东西

​ 【FROM 表名】;

​ 类似于Java中 :System.out.println(要打印的东西);

​ 特点:

​ ①通过select查询完的结果 ,是一个虚拟的表格,不是真实存在

​ ② 要查询的东西 可以是常量值、可以是表达式、可以是字段、可以是函数

*7.********2********、********条件查询*

​ 条件查询:根据条件过滤原始表的数据,查询到想要的数据

​ 语法:

​ select

​ 要查询的字段|表达式|常量值|函数

​ from

​ 表

​ where

​ 条件 ;

*7.2.1、********分类*

​ 一、条件表达式

​ 示例:salary>10000

​ 条件运算符:

​ > < >= <= = != <>

​ 二、逻辑表达式

​ 示例:salary>10000 && salary<20000

​ 逻辑运算符:

​ and(&&):两个条件如果同时成立,结果为true,否则为false

​ or(||):两个条件只要有一个成立,结果为true,否则为false

​ not(!):如果条件成立,则not后为false,否则为true

​ 三、模糊查询

​ 示例:last_name like ‘a%’

​ %:代表任意多个字符,也包括0个字符

​ _:代表一个字符,如果要查询的内容也带下划线,例如一个名字为K_ing,要查询名字的第二个字符为_的人,就要用到转义字符,

​ 可以用\也就是last_name like ‘__%’

​ 或者也可以自定义一个KaTeX parse error: Expected group after '_' at position 19: …ast_name like '_̲_%’ escape ' ′ , 这 是 自 定 义 个 转 义 符 的 意 思 , ' ,这是自定义个转义符的意思, ′,这是自定义个转义符的意思,可以替换为任意一个随便的字符,只需要在后面用escape 解释一下即可。

​ 四、between and

​ 示例:要查询部门编号在100到120之间的员工信息

​ 以前:

​ select * from employees where department-id>=100 and department-id<=120;

​ 现在:

​ select * from employees where department-id between 100 and 120;

​ 注意:

​ 1、使用between and 可以提高语句的简洁度

​ 2、between and 可以包含临界值,完全等价于 >= <=

​ 3、既然完全等价于>= <= 也就意味着,between 和 and 前后的值不能乱写,例如本来是between 100 and 120,就不能写成 between 120 and 100。虽然不会报错,但是查询不到结果

五、in关键字(离散查询)

含义:判断某一字段的值是否属于in列表中的某一项

特点:

1:使用in提高了语句的简洁度

2:in列表的值类型必须统一或兼容

3:in列表里面不能用通配符%或者_

​ 例子:查询员工的工种编号是 itcast 、 itheima 中的一个员工名

以前:Select last-name from employees where job-id = itcast or job-id = itheima

现在:Select last-name from employees where job-id in( ‘itcast ’, ’itheima’);

六、is null 和 is not null

意义:=或者<>不能用于判断null值

Is null 和 is not null 可以判断null值

例子:查询没有奖金的员工名

Select last-name from employees where bonus is null

例子:查询有奖金的员工名

Select last-name from employees where bonus is not null

七、*安全等于 <=>*

*意义:用于判断是否等于*

例子:查询没有奖金的员工名

Select last-name from employees where bonus <=> null

*8、********排序查询*

​ 1、语法:

​ select

​ 要查询的东西

​ from

​ 表

​ where

​ 条件

​ order by 排序的字段|表达式|函数|别名 【asc|desc】

2、特点:

1、Asc是自然顺序,也就是从小到大,desc是从大到小,如果不写,默认是升序

2、order by 子句中可以支持单个或多个字段、表达式、函数、别名

3、order by 子句一般是放在查询语句的最后面,limit子句除外

3、例子:查询员工信息,工资按从高到低排序

Select * from employees order by salary desc

例子:查询员工信息,先按工资排序,再按部门编号排序(order by 支持多个字段排序)

Select * from employees order by salary asc,department-id desc;

*9、********常见函数*

概念:类似于java中的方法,将一组逻辑语句封装在方法体中,对外暴露方法名

好处:1、隐藏了具体的功能实现细节;2、提高代码的重用性

调用:select 函数名(实参列表) from 表;在函数里面的参数用到表中的字段时,才需要加from 表。

特点:

1、叫什么(函数名)

2、干什么(函数功能)

*9.1、********单行函数*

*9.1.1********、字符函数*

​ ·concat拼接

Select concat(lastname,firstname) from employees;

​ ·Substr或者substring截取子串

Select substr(‘李莫愁爱上了陆展元’,6);

结果显示:了陆展元

注意:这里的索引是从1开始的,就写一个默认是从6开始之后的全部字符

Select substr(‘李莫愁爱上了陆展元’,1,3);

结果显示:李莫愁

注意:这里的1代表从第一个开始,是索引,3代表字符的长度,不是索引。

例子:名字首字母大写,其他小写,用‘_’拼接

Select concat(upper(substr(lastname,1,1),‘_’,lower(lastname,2))) from employees;

​ ·upper转换成大写

Select upper(‘fuxupeng’)

​ ·lower转换成小写

Select lower(‘FUXUPENG’)

示例:将姓大写,名小写,然后拼接

Select cancat(upper(lastname),lower(firstname))from employees;

​ ·trim去前后指定的空格和字符

Select trim (‘ 欧阳 ’);

显示结果:欧阳

空格会消失

Select trim(‘a’from ‘aaaaa欧aa阳aaaaa’);

显示结果:欧aa阳

这样可以去除指定的字符,但是只能去前后的字符

​ ·ltrim去左边空格

Select ltrim(‘ 欧阳 ’);

显示结果:欧阳 ;

欧阳后面还有三个空格不能去除

​ ·rtrim去右边空格

Select rtrim(‘ 欧阳 ’);

显示结果: 欧阳;

前面的空格不能去除

​ ·replace替换

Select replace(‘欧阳茜是憨憨’,‘憨憨’,‘憨瓜’);

显示结果:欧阳茜是憨瓜

​ ·lpad用指定字符实现左填充指定长度

Select lpad(‘欧阳’,10,‘*’);

显示结果:********欧阳

欧阳只有两个字符,不满10个,所以在左边填充8个*

Select lpad(‘欧阳’,1,‘*’);

显示结果:欧

最终显示的长度由里面的数字决定

​ ·rpad用指定字符实现右填充指定长度

Select rpad(‘欧阳’,10,‘*’);

显示结果:欧阳********

欧阳只有两个字符,不满足10个,所以在右边填充8个*

​ ·instr返回子串第一次出现的索引,如果找不到,返回0

Select instr(‘欧阳茜是个憨憨’,‘憨憨’);

显示结果:6

Select instr(‘憨憨欧阳茜’,‘憨憨’);

显示结果:1

​ ·length 获取字节个数:

Select length(参数) from 表;如果参数用到表中的字段就加from 表,没有就不加

Ifnull 判断是否为空(因为null+任何东西都为null,当某个字段有些为null,有些 不为null的时候,就要加这个函数用于判断)

*9.1.********2、数学函数*

​ ·round 四舍五入

Select round(1.65);

显示结果:2

Select round(1.567,2);

显示结果:1.57

小数点后保留两位

​ ·rand 获取随机数,返回0-1之间的小数

​ ·floor向下取整(显示小于等于这个数的最大整数)

select floor(1.02)

显示结果:1

Select floor(-1.02)

显示结果:-2

​ ·ceil向上取整(显示大于等于这个数的最小整数)

Select ceil(1.52)

显示结果:2

Select ceil(1.02)

显示结果:2

Select ceil(1.00)

显示结果:1

Select ceil(-1.2)

显示结果:0

​ ·mod取余(带负数的求余,只要看被除数就行,余数跟被除数的符号一致)

mod运算:mod(a,b): a-a/b*b

Mod(-10,-3):-10-(-10)/(-3)*(-3)=-1

Select mod(10,3)

显示结果:1

Select mod(-10,-3);

显示结果:-1

Select mod(-10,3);

显示结果:-1

Select mod(10,-3);

显示结果:1

​ ·truncate截断

Select truncate(1.65,1);

显示结果:1.6

不管后面是什么,就取到小数点后几位

*9.1.********3、日期函数*

​ ·now当前系统日期+时间

Select now();

显示结果:当前计算机的日期和时间

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pZ6IpEfL-1614847542256)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps286.jpg)]

​ ·curdate当前系统日期,不包含时间

Select curdate();

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8ExcC1ng-1614847542258)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps287.jpg)]

​ ·curtime当前系统时间,不包含日期

Select curtime();

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CduXYgVM-1614847542259)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps288.jpg)]

·可以获取指定的部分,年、月、日、时、分、秒

Select year(now())as 年;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GBTrS4ve-1614847542261)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps289.jpg)]

Select year(‘1997-08-24’)as 欧阳茜的生日啊;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LOG8RGJD-1614847542263)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps290.jpg)]

Select year(某个表的字段名);

Select month(now());

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JCym7dZS-1614847542264)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps291.jpg)]

Select monthname(now());

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vBHf3bxs-1614847542265)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps292.jpg)]

Select day(now());

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-T3pTtCiI-1614847542265)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps293.jpg)]

Select hour(now());

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rLZkeV9M-1614847542266)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps294.jpg)]

​ ·str_to_date 将日期格式的字符转换成指定格式的日期

select str_to_date(‘1997-08-24’,‘%Y-%c-%d’)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TgkwwJiQ-1614847542267)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps295.jpg)]

Str_to-date是转化成日期,所以必须是日期格式,不能加上年月日的字符

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eV8ewvb0-1614847542267)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps296.jpg)]

​ ·date_format将日期转换成字符

Select date_format(now(),‘%y年%c月%d日’);

这个是日期转换成字符,最后是转化成字符的,所以可以加上年月日字符。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HhCmC7Oy-1614847542268)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps297.jpg)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LSkxMA7R-1614847542269)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps298.jpg)]

*9.1.********4、流程控制函数*

​ ·if 处理双分支(类似于java的三元运算符)

如果前面的表达式正确,则显示第二个值,否则显示第三个值

Select if(10>5,‘大’,‘小’);

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-feo85wSw-1614847542269)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps299.jpg)]

​ ·case语句 处理多分支

​ 用法1:等值判断

*Java中的switch case:*

Switch 要判断的变量或表达式

Case 常量1:语句1;break;

Default:语句n;break;

*Mysql中:*

Case 要判断的字段或表达式

When 常量1 then 要显示的值1或语句1;

When 常量2 then 要显示的值2或语句2;(这里的分号,显示的是语句时才要加分号)

Else 要显示的值n或语句n

End

例子:查询员工的工资,要求:

部门号=30,显示的工资为1.1倍

部门号=40,显示的工资为1.2倍

部门号=50,显示的工资为1.3倍

其他部门,显示为原工资

Select salary as 原始工资,dapartment_id

Case department_id

When 30 then salary*1.1(注意这是个值,所以不能加分号,不然到这里就结束了)

Case department_id

When 40 then salary*1.2

Case department_id

When 50 then salary*1.3

Else salary

End as 新工资

From employees;

*用法2:类似于多重if*

Java中:

If(条件1){

语句1;

}else if(条件2){

语句2;

}

Else{

语句n;

}

Mysql中:

Case

When 条件1 then 要显示的值1或语句1

When 条件2 then 要显示的值2或语句2

Else 要显示的值n或语句n

例子:查询员工的工资情况

如果工资>20000,显示A级别

如果工资>15000,显示B级别

如果工资>10000,显示C级别

否则,显示D级别

Select salary,

Case

When salary>20000 then‘A’

When salary>15000 then‘B’

When salary>10000 then‘C’

Else ‘D’

End as 工资级别

From employees;

*9.1.********5、其他函数*

​ ·version版本

Select version();

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HGTbeyTO-1614847542270)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps300.jpg)]

​ ·database当前库

select database();

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ItwufYgA-1614847542290)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps301.jpg)]

​ ·user当前连接用户

select user();

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cbX8avpn-1614847542290)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps302.jpg)]

*9.2、********分组函数*

1、功能:用作统计使用,又称为聚合函数或者统计函数或组函数

*9.2.1、********sum 求和*

Select sum(salary) from employees;

求某个字段的和

*9.2.2、********max 最大值*

Select max(salary) from employees;

求某个字段的最大值

*9.2.3、********min 最小值*

Select min(salary) from employees;

求某个字段的最小值

*9.2.4、********avg 平均值*

Select avg(salary) from employees;

求某个字段的平均值

*9.2.5、********count 计数*

Select count(salary) from employees;

求某个字段的个数

*9.2.6、多个一起使用*

select sum(salary) as 和,avg(salary) as 平均值,max(salary) as 最大值…

*9.2.7、********特点:*

​ 1、以上五个分组函数都忽略null值,除了count(*)

​ 2、sum和avg一般用于处理数值型

​ max、min、count可以处理任何数据类型

​ 3、都可以搭配distinct使用,用于统计去重后的结果

​ 4、count的参数可以支持:

​ 字段、、常量值,一般放1(***其实任何值都可以,哪怕是字符型都可以,它相**** *当于在每一行的前面都加了一个1或者字符,然后count统计的时候,是在统计1* ****的个数或者字符的个数***),建议使用 count()

在MYISAM的引擎下,count(*)的效率要高,在MYISAM的内部就有一个计数器,可以直接返回。

在INNODB的引擎下,count(*)和count(1)的效率差不多,比count(字段)要高

****5、注意:****和分组函数一同查询的字段有限制

Select avg(salary),last-name from employees;

虽然这个语句不会报错,但是avg是求平均值,所以最后查询出来的只有一行,数据库显示的结果是规则的表格,所以last-name也会出现一个值,正常来说,avg只有一个数值,但是last-name应该有所有的值,但是为了规则表格,last-name也只显示了一个值,但是这个值是没有任何意义的。

*和分组函数一同查询的字段要求是group by后的字段,其他的都不行。*

*10、查询*

*10.1、********分组查询********(group by)*

*10.1.1、********语法:*

​ select 分组函数,查询的字段(要求出现在group by后面)

​ from 表

【where 条件】

​ group by 分组的字段

【order by子句】

*10.1.2、举例*

例子1:查询每个工种的最高工资

Select max(salary),job-id

From employees

Group by job-id

例子2:查询不同的部门个数

Select count(*),department-id

From employees

Group by department-id

*10.1.3、添加分组前的筛选条件*

例子1:查询邮箱中包含A字符的,每个部门的平均工资

Select avg(salary),department-id

From employees

Where email like‘%A%’
group by department-id

例子2:查询有奖金的每个领导手下员工的最高工资

Select max(salary),manager-id

From employees

Where bonus is not null

Group by manager-id

*10.1.4、添加分组后的筛选条件*

例子1:查询哪个部门的员工人数大于2

1、查询每个部门的员工人数

Select count(*),department-id

From employees

Group by department-id;

2、根据1的结果进行筛选,查询哪个部门的员工人数大于2

Select count(*),department-id

From employees

Group by department-id

Having count(*)>2;

****注意:****这里不能用where条件,比如说:

Select count(*),department-id

From employees

Where count(*)>2

Group by department-id;

Where后面的条件必须是那张表里面有这个字段,而count(*)这个字段在原始表里面并不存在,所以不能用where筛选条件,只能在后面用having

例子:查询每个工种有奖金的员工的最高工资>12000的工种编号及其最高工资

1、查询每个工种有奖金的员工的最高工资

Select max(salary),job-id

From employees

Where bonus is not null

Group by job-id;

这里的bonus在原始表中是存在的,所以可以使用where条件

2、根据1的结果继续筛选,最高工资>12000

Select max(salary),job-id

From employees

Where bonus is not null

Group by job-id

Having max(salary)>12000;

这里的max(salary)在原始表中并不存在,所以只能使用having

3、按表达式或者函数分组

例子:按员工姓名长度分组,查询每一组的员工人数,筛选员工人数>5的有哪些

1、查询每个长度的员工人数

select count(*),length(last-name)

From employees

Group by length(last- name);

2、添加筛选条件

select count(*)as 人数,length(last-name)

From employees

Group by length(last- name)

Having 人数>5;

注意:Having是支持别名的

4、按多个字段分组

例子:查询每个部门每个工种员工的平均工资

Select avg(salary),department-id,job-id

From employees

Group by department-id,job-id;

注意:Group by 后面的字段顺序可以改变

*10.1.5、特点*

​ 1、可以按单个字段分组

​ 2、和分组函数一同查询的字段最好是分组后的字段

​ 3、分组筛选

​ 针对的表 位置 关键字

​ 分组前筛选: 原始表 group by的前面 where

​ 分组后筛选: 分组后的结果集 group by的后面 having

​ 4、可以按多个字段分组,字段之间用逗号隔开,没有顺序要求,也可以支持表达式和 函数

​ 5、可以支持排序(order by子句放在最后)

​ 6、having后可以支持别名

7、分组函数做条件一定是放在having子句中

8、能用分组前筛选的就优先使用分组前筛选,性能会高一点

*10.1.6、添加排序*

例子:查询每个部门每个工种员工的平均工资,并且按平均工资的高低进行排序

Select avg(salary),department-id,job-id

From employees

Group by department-id,job-id

Order by avg(salary) desc;

*10.2、********多表连接查询*

当要查询的字段来自于多个表时,就会用到连接查询。

*10.2.1、出现的问题:*

​ 笛卡尔乘积现象:表1 有m行,表2 有n行,结果=n*m行

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cBPr7lk2-1614847542291)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps303.jpg)]

比如:有一张表是女生有n个,一张表是男生有m个,一个女生匹配一个男朋友,

这时候,如果这样去写sql语句:

Select name,boyname from girls,boys

这样写是不会不错的,也能查询出结果,只是这个结果是没有意义的。

结果显示:一个女生去匹配每一个男朋友,也就是说一个女生有m个男朋友,这 样就与原来的要求不符合。

​ 解决办法:添加上连接条件

Select name,boyname from girls,boys

Where girls.boyfriend-id=boys.id;

*注意:这里的连接条件不是乱加的,这个连接条件要么是两张表里面一样的字段,要* *么就是某一个表中的某个字段匹配值来自于另一张表的某个字段,也就是两* *张表中的两个字段要产生联系才行。*

*10.2.2、分类*

*10.2.2.1、按年代分*

Sql92标准:仅支持内连接

Sql99标准:(推荐使用)支持内连接+外连接(左外、右外)+交叉连接

*10.2.2.2、sql92标准*

内连接:

·等值连接

例子1:查询女生名和对应的男生名

Select name,boyname from girls,boys

Where girls.boyfriend-id=boys.id;

例子2:查询员工名和对应的部门名

Select last-name,department-name from employees,departments

Where employees.department-id=departments.department-id;

*每次我们查询的时候都要用表名.字段名,如果查询的表多了,而且表名* *可能都比较长,这样就会很麻烦,所以我们可以为表起一个别名。*

例子3:查询员工名、工种号、工种名

Select last-name,employees.job-id,job-title

From employees,jobs

Where employees.job-id=jobs.job-id

可以写成:

Select last-name,e.job-id,job-title

From employees as e,jobs as j

Where e.job-id=j.job-id

*注意:如果表使用了别名,那查询的字段就不能用原来的表名去限定,否则报错,from后面的表的顺序可以随意*

等值连接可以加筛选吗?

例子4:有奖金的员工名、部门名

Select last-name,department-id

From employees e,departments d

Where e.department-id=d.department-id

And e.bonus is not null;

*·非等值连接*

例子:查询员工的工资和工资级别

Select salary,grade-level

From employees e,job-grades j

Where salary between j.lowest-sal and j.hightest-sal;

·自连接:与等值连接类似,只是自连接是在一张表中的。

注意:传统模式下的连接 :等值连接——非等值连接

​ 1.等值连接的结果 = 多个表的交集

​ 2.n表连接,至少需要n-1个连接条件

​ 3.多个表不分主次,没有顺序要求

​ 4.一般为表起别名,提高阅读性和性能

*10.2.2.3、********sql99语法:通过join关键字实现连接*

语法:

select 查询列表

​ from 表1 【连接类型】 (连接类型就是内连接,外连接这些类型)

​ 【inner|left outer|right outer|cross】join 表2 on 连接条件

​ 【inner|left outer|right outer|cross】join 表3 on 连接条件

​ 【where 筛选条件】

​ 【group by 分组字段】

​ 【having 分组后的筛选条件】

​ 【order by 排序的字段或表达式】

​ 含义:1999年推出的sql语法

​ 支持:

​ 内连接:inner(等值连接、非等值连接)

​ 外连接(左外(left 【outer】)、右外(right 【outer】)、全外(full 【outer】))

​ 交叉连接(cross)

*10.2.2.3.1、sql99内连接*

1、语法:

Select 查询列表

From 表1 别名

Inner join 表2 别名

On 连接条件;

2、分类

·等值连接:

例子1:查询员工名,部门名

Select last-name,department-name

From employees e

Inner join departments d

On e.department-id=d.department-id;

例子2:查询名字中包含e的员工名和工种名(添加筛选)

Select last-name,job-title

From employees e

Inner join jobs j

On e.job-id=j.job-id

Where e.last-name like(%e%);

例子3:查询部门个数>3的城市名和部门个数(分组加筛选)

Select city,count(*)部门个数

From departments d

Inner join locaotions l

On d.locaotion-id=l.location-id

Group by city

Having 部门个数>3;

例子4:查询哪个部门的部门员工个数>3的部门名和员工个数,并按个数降序

1、查询每个部门的员工个数

Select count(*),department-name

From employees e

Inner join departments d

On e.department-id=d.department-id

Group by department-name;

2、在1的基础上筛选员工个数>3的记录,并排序

Select count(*),department-name

From employees e

Inner join departments d

On e.department-id=d.department-id

Group by department-name

Having count(*)>3

Order by count(*)asc;

例子5:查询员工名、部门名、工种名、并按部门名排序(三表连接)

Select last-name,department-name,job-title

From employees e

Inner join departments d

On e.department-id=d.department-id

Inner join jobs j

On e.job-id=j.job-id

Order by department-name desc;

特点:

1、可以添加排序,分组,筛选

2、Inner可以省略

3、筛选条件放在where后面,连接条件放在on的后面,提高分离性,便于 阅读

4、Inner join连接和sql92语法中的等值连接最终的效果是一样的

·非等值连接:

例子1:查询员工的工资级别

Select salary,grade-level

From employees e

Inner join job-grades j

On e.salary between j.loweat-sal and j.hightest-sal;

例子2:查询工资级别的个数>2的个数,并按工资级别降序

Select count(*),grade-level

From employees e

Inner join job-grades j

On e.salary between j.loweat-sal and j.hightest-sal;

Groupe by grade-level

Having count(*)>2

Order by grade-level desc;

·自连接:

案例:查询员工名和直接上级的名称

sql99语法:

​ SELECT e.last_name,m.last_name

​ FROM employees e

​ JOIN employees m ON e.manager_id=m.employee_id;

sql92:语法

​ SELECT e.last_name,m.last_name

​ FROM employees e,employees m

​ WHERE e.manager_id=m.employee_id;

*10.2.2.3.2、sql99外连接*

1、应用场景:用于查询一个表中有,另一个表中没有的记录

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YQfI8r17-1614847542292)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps304.jpg)]

这两个表中有关系的字段就是beauty表中的boyfriend-id和boys表中的id,这两个可以相对应,但是,boyfriend-id有8,9这两个值,但是在boys表中却没有,这个时候就可以用到外连接。

*2、特点:*

1、外连接的查询结果为主表中的所有记录,如果从表中有和主表匹配的,则显示

如果从表中没有和主表匹配的,则显示null

外连接的查询结果=内连接查询结果+主表中有而从表中没有的记录

2、如何区分主从表

左外连接:left join左边的是主表

右外连接:right join右边的是主表

3、左外和右外交换两个表的顺序,可以实现同样的效果

4、全外连接=内连接的结果+表1中有但表2中没有的记录+表1中没有但表2中有的记 录

3、引入:查询男朋友不在男生表中的女生名

左外连接:

Select b.name,bo.*

From beauty b

Left outer join boys bo

On b.boyfriend-id=bo.id;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dQkRUfF0-1614847542292)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps305.jpg)]

右外连接:

Select b.name,bo.*

Fromboys bo

Right outer join beauty b

On b.boyfriend-id=bo.id;

*10.2.2.3.3、全外连接*

全外连接可以查出三部分,一部分是多表中的交集部分,一部分是主表中不能与从表匹配到记录,还有一部分是从表中不能与主表匹配到的部分。

*10.2.2.3.4、交叉查询*

Select b.,bo.

From beauty b

Cross join boys bo;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Xa20cPci-1614847542293)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps306.jpg)]

交叉查询其实就是一个笛卡尔乘积,女生表有11行数据,男生表有4行数据,用了交叉查询后就是44行数据。

*10.3、********子查询*

*10.3.1、********含义*

出现在其他语句(包括所有的语句)中的select语句,称为子查询或内查询

在外面的查询语句,称为主查询或外查询

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jTLt5vtg-1614847542293)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps307.jpg)]

*10.3.2、分类*

1、按子查询出现的位置

Select后面:

只支持标量子查询

From后面:

表子查询

Where或者having后面:

标量子查询

列子查询

行子查询(用的比较少)

Exists后面(相关子查询)

表子查询

2、按结果集的行列数不同

标量子查询(结果集只有一行一列)

列子查询(结果集只有一列多行)

行子查询(结果集可以是多行多列,主要是一行多列)

表子查询(结果集随意,只要是查询结果即可,一般Wie多行多列)

*10.3.3、where和having后面*

*·1、标量子查询(单行子查询)*

例子:谁的工资比Abel高

(1)、查询名字为Abel的工资

Select salary from employees where last-name=‘Abel’;

(2)、查询员工信息,满足salary>1出现的结果

Select * from employees where salary>(Select salary from employees where last-name=‘Abel’);

例子2:返回job-id和141号员工相同,salary比143号员工多的员工姓名,job-id 和工资

(1)、查询141号员工的job-id

Select job-id from employees where employee-id=141;

(2)、查询143号员工的工资

Select salary from employees where employee-id=143;

(3)、查询员工的姓名,job-id,工资,要求jiob-id=(1)的结果且salary>(2)的结 果

Select last-name,job-id,salary from employees where job-id=(Select job-id from employees where employee-id=141) and salary>(Select salary from employees where employee-id=143);

例子3:返回公司工资最少的员工的last-name,job-id,salary

(1) 、查询公司的最低工资

Select min(salary)from employees;

(2) 、查询员工的last-name,job-id,salary,要求salary=(1)的结果

Select last-name,job-id,salary from employees where salary=(Select min(salary)from employees);

例子4:查询最低工资>50号部门最低工资的部门id和其最低工资

(1) 、查询50号部门的最低工资

Select min(salary)from employees where department-id=50;

(2) 、查询每个部门的最低工资

Select min(salary),department-id from employees

Group by department-id;

(3) 、在(2)基础上进行筛选,满足min(salary)>(1)的结果

Select min(salary),department-id from employees

Group by department-id

Having min(salary)>(Select min(salary)from employees where department-id=50);

*·2、列子查询(多行子查询)*

例子1:返回locaotion-id是1400或1700 部门中的所有员工姓名

(1) 、查询location-id是1400或1700的部门编号

Select department-id from departments where location-id in(1400,1700);

(2) 、查询员工姓名,要求部门号是(1)列表中的某一个

Select last-name from employees where department-id in(Select department-id from departments where location-id in(1400,1700));

例子2:返回其他工种中比job-id为‘it-prog’工种任一工资低的员工的员工号、 姓名、job-id以及salary

(1) 、查询job-id为‘it-prog’部门的任一工资

Select salary from employees where job-id=‘it-prog’;

(2) 、查询员工号、姓名、job-id以及salary,要求salary<(1)的结果

Select last-name,employee-id,job-id,salary from employees where salary <any(Select salary from employees where job-id=‘it-prog’)and job-id<> ‘it-prog’;

例子3:返回其他工种中比job-id为‘it-prog’工种所有工资都要低的员工的员工 号、姓名、job-id以及salary

Select last-name,employee-id,job-id,salary from employees where salary <all(Select salary from employees where job-id=‘it-prog’)and job-id<> ‘it-prog’

*·3、行子查询(结果集一行多列或多行多列)*

例子1:查询员工编号最小并且工资最高的员工信息

*以前:*

(1) 、查询员工编号最小

Select min(employee-id)from employees;

(2) 、查询最高工资

Select max(salary)from employees;

(3) 、查询员工信息

Select * from employees where employee-id=(Select min(employee-id) from employees) and salary=(Select max(salary)from employees);

*现在:*

Select * from employees where(employee-id,salary)=(select min (employee-id),max(salary)from employees);

*注意:行子查询有局限性,只有当要查询的语句都是用“=”的时候,才能使* *用行自查询,所以一般不用行子查询。*

*特点:*

​ 1、子查询都放在小括号内

​ 2、子查询可以放在from后面、select后面、where后面、having后面,但一般放在条件的右侧

​ 3、子查询优先于主查询执行,主查询使用了子查询的执行结果

​ 4、子查询根据查询结果的行数不同分为以下两类:

​ ① 单行子查询

​ 结果集只有一行

​ 一般搭配单行操作符使用:> < = <> >= <=

*非法使用子查询的情况:*

​ a、子查询的结果为一组值

​ b、子查询的结果为空

​ ② 多行子查询

​ 结果集有多行

​ 一般搭配多行操作符使用:any、all、in、not in

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-O83qGtg4-1614847542294)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps308.jpg)]

​ in: 属于子查询结果中的任意一个就行

​ any和all往往可以用其他查询代替

*10.3.4、select后面(只支持标量子查询)*

例子1:查询每个部门的员工个数

Select d.,(select count() from employees e

Where e.department-id=d.department-id )

From departments d;

例子2:查询员工号=102的部门名

用****连接查询****也可以实现:

Select (select department-name

from departments d

inner join employees e

On d.department-id = e.department-id

where e.employee-id =102 );

*10.3.5、from后面*

例子:查询每个部门的平均工资的工资等级

(1)、查询每个部门的平均工资

Select avg(salary),department-id

From employees

Group by department-id;

(2)、查询工资登记

Select * from job-grades;

(3)、连接(1)和(2)的结果集,筛选条件平均工资between lowest-sal and highest-sal

Select ag-dep.*,grade-level

From (Select avg(salary)ag,department-id

From employees

Group by department-id

)ag-dep

Inner join job-grades g

On ag-dep.ag between lowest-sal and highest-sal;

*10.3.6、exists后面(相关子查询)*

Select exists(select employee-id from employees);

查询结果为1

Select exists(select employee-id from employees where salary=3000000000);

查询结果为0

这个查询只判断子查询的结果是否存在,存在就显示,不存在就显示0

例子1:查询有员工的部门名

Select department-name

From departments d

Where exits(

Select * from employees e

Where e.department-id = d.department-id

);

*10.4、********分页查询*

*10.4.1、********应用场景*

​ 实际的web项目中需要根据用户的需求提交对应的分页查询的sql语句,一页显示不全

*10.4.2、********语法*

​ select 字段|表达式,…

​ from 表

【jion type】 jion 表2

​ 【where 条件】

​ 【group by 分组字段】

​ 【having 条件】

​ 【order by 排序的字段】

​ limit 【起始的条目索引】条目数;

*10.4.3、例子*

·查询前5条员工信息

Select * from employees limit 0,5;

如果起始的索引是从第一条开始的,可以省略0:

Select * from employees limit 5;

·查询第11到25条员工信息

Select * from employees limit 10,15;

·有奖金的员工信息,并且工资较高的前10名

Select * from employees where bonus is not null

Order by salary desc

Limit 10;

*10.4.4、********特点*

​ 1.起始条目索引从0开始

​ 2.limit子句放在查询语句的最后

​ 3.公式:select * from 表 limit (page-1)*sizePerPage,sizePerPage

​ 假如:

​ 每页显示条目数sizePerPage

​ 要显示的页数 page

*10.5、********联合查询*

*10.5.1、********引入*

​ union 联合、合并,讲多条查询语句的结果合并为一个结果

*10.5.2、********语法*

​ select 字段|常量|表达式|函数 【from 表】 【where 条件】 union 【all】

​ select 字段|常量|表达式|函数 【from 表】 【where 条件】 union 【all】

​ select 字段|常量|表达式|函数 【from 表】 【where 条件】 union 【all】

​ …

​ select 字段|常量|表达式|函数 【from 表】 【where 条件】

*10.5.3、例子*

查询部门编号>90或邮箱包含‘a’的员工信息

以前:select * from employees where email like ‘%a%’ or department-id>90;

现在:select * from employees where email like‘%a%’

Union

Select * from employees where department-id > 90;

*注意:当要查询的字段来自多个表,且这两个表没有联系(之前学习的join 也可以连接两张表,但是用join之类的关键词所连接的两张表是有联系的,他们的某一个字段是相同的,而这里的union是当两张表完全没有任何联系的时候,才使用),但是查询的信息一致时(例如,有国外和国内的人们的一张表,要查询国外的男性人数和国内的男性人数,当查询的信息一致时,才可使用),可以用union关键字将他们的查询结果合并为一个查询结果,方便查看。*

*10.5.4、********特点*

​ 1、多条查询语句的查询的列数必须是一致的(select后面查询的字段要一样多)

​ 2、多条查询语句的查询的列的类型和顺序最好相同

​ 3、union代表去重,union all代表不去重(查询的两个表中可能有重复的)

*三、********DML语言*

*1、********插入*

*1.1、********语法*

​ insert into 表名(字段名,…)

​ values(值1,…);

*1.2、********特点*

​ 1、字段类型和值类型一致或兼容,而且一一对应

​ 2、可以为null的字段,可以不用插入值,或用null填充

​ 3、不可以为null的字段,必须插入值

​ 4、字段个数和值的个数必须一致

​ 5、如果insert into 后面的字段名省略了,那values里面的值就要把这张表里面的所有 都添加一遍,且要按字段顺序和类型一次添加。

*2、********修改*

修改单表语法:重点

​ update 表名 set 字段=新值,字段=新值

​ 【where 条件】

修改多表语法:非重点

*Sql92:*

​ update 表1 别名1,表2 别名2

​ set 字段=新值,字段=新值

​ where 连接条件

​ and 筛选条件

*Sql99:*

Update 表1 别名

Inner/left/right join 表2 别名

On 连接条件

Set 字段=新值,…

Where 筛选条件

例子:修改张无忌的女朋友的手机号为114

Update boys bo

Inner join beauty b

On bo.‘id’ = b.‘boyfriend-id’

Set b.‘phone’=‘114’

Where bo.‘boyname’=‘张无忌’;

*3、********删除*

方式1:delete语句

单表的删除: *★*

​ delete from 表名 【where 筛选条件】

多表的删除:****不重要****

​ delete 别名1,别名2

​ from 表1 别名1,表2 别名2

​ where 连接条件

​ and 筛选条件;

方式2:truncate语句

​ truncate table 表名

两种方式的区别【面试题】

​ 1.truncate不能加where条件,而delete可以加where条件

​ 2.truncate的效率高一丢丢

​ 3.truncate 删除带自增长的列的表后,如果再插入数据,数据从1开始

​ delete 删除带自增长列的表后,如果再插入数据,数据从上一次的断点处开始

\4. truncate删除不能回滚,delete删除可以回滚

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Yd4seW81-1614847542294)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps309.jpg)]

*四、********DDL语句*

*1、********库和表的管理*

*1.1、********库的管理*

​ 一、创建库

​ create database 【if not exists】 库名

​ 二、删除库

​ drop database 【if exists】库名

三、更改库的字符集(暂时更改)

Alter database 库名 character set 字符集

想要永久改变字符集,需要在配置文件里面修改

*1.2、********表的管理*

1、语法:

CREATE TABLE IF NOT EXISTS 表名(

字段名 字段的类型【长度,约束】,

字段名 字段的类型【长度,约束】,

字段名 字段的类型【长度,约束】

);

2、创建表

​ CREATE TABLE IF NOT EXISTS stuinfo(

​ stuId INT,

​ stuName VARCHAR(20),

​ gender CHAR,

​ bornDate DATETIME

​ );

​ DESC studentinfo;

*2********、********修改表 alter*

语法:ALTER TABLE 表名 ADD|MODIFY|DROP|CHANGE COLUMN 字段名 【字段类型,约束】;

*注意:COLUMN可以省略不写,建议都写上*

​ #①修改字段名

ALTER TABLE 表名 CHANGE COLUMN 原字段名 修改的字段名 字段类型;

​ ALTER TABLE studentinfo CHANGE COLUMN sex gender CHAR;

​ #②修改表名

ALTER TABLE 原表名 RENAME [TO] 修改的表名;

​ ALTER TABLE stuinfo RENAME [TO] studentinfo;

​ #③修改字段类型和列级约束

ALTER TABLE 表名 MODIFY COLUMN 字段名 字段类型 ;

​ ALTER TABLE studentinfo MODIFY COLUMN borndate DATE ;

​ #④添加字段

​ ALTER TABLE 表名 ADD COLUMN 字段名 字段类型 【first】;

​ ALTER TABLE studentinfo ADD COLUMN email VARCHAR(20) 【first】;

*注意:加了first 就是在所有字段的最前面添加这个字段*

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-x6ukVaBC-1614847542294)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps310.jpg)]

​ #⑤删除字段

ALTER TABLE 表名 DROP COLUMN字段名;

​ ALTER TABLE studentinfo DROP COLUMN email;

*3********、********删除表*

​ DROP TABLE [IF EXISTS] studentinfo;

*4、表的复制*

*4.1、仅复制表的结构*

Create table 新表名 like 被复制的表名

Create table copy like books;

*4.2、复制表的结构+数据*

Create table 新表名

Select * from 被复制的表名;

Create table copy2 select * from books;

*4.3、复制部分数据*

Create table 新表名

Select 要复制的字段名1,… from 被复制的表名 where 筛选条件;

Create table copy3 select id,name,author from books where sex=‘男’;

*4.4、仅仅复制某些字段,不要数据*

Create table 新表名

Select 要复制的字段名1,…

From 被复制的表名

Where 1=2;

Create table copy4 select id,name from books where 1=2;

*在where条件恒为假的时候,数据是不会复制过去的*

*4.5、蠕虫复制*

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kP68rOP1-1614847542295)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps311.jpg)]

*5、********常见类型*

*数值型*

​ 整型:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vi8CyuEd-1614847542296)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps312.jpg)]

​ 小数:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MKgICaR7-1614847542296)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps313.jpg)]

注意:M和D的意思(M和D可以省略不写)

M:整数部分+小数部分一共的长度

D:小数部分的长度

如果超过了范围,则插入临界值

*字符型:*

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TMUYO5J7-1614847542296)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps314.jpg)]

较短的文本:char,varchar

1、M的意思是最大字符数,char的M可以省略不写,默认为1,varchar不能 省略。

2、Char是固定长度的字符,耗空间,高性能;varchar是可变长度的字符,省 空间,低性能。

较长的文本:text,Blob类型(较长的二进制数据)

*日期型:*

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mHEsmJck-1614847542297)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps315.jpg)]

Datetime和timestamp的区别:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-avYnw5os-1614847542297)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps316.jpg)]

*6、********常见约束*

*6.1、含义*

一种限制,用于限制表中的数据,为了保证表中数据的准确性和可靠性

*6.2、分类*

​ NOT NULL 非空,用于保证该字段的值不能为空

​ DEFAULT 默认,用于保证该字段的默认值

​ UNIQUE 唯一,用于保证该字段的值具有唯一性,可以为空

​ CHECK 检查,(mysql中不支持)

​ PRIMARY KEY 主键:用于保证该字段的值具有唯一性,不能重复,且非空

​ FOREIGN KEY 外键:用于限制两个表的关系,用于保证该字段值必须来自于主 表的关联列的值,在从表添加外键约束,用于引用主表中 某列的值

Comment 说明:用于对该字段进行注释说明

Auto_increment 自增:用于该字段自动增加其值

Zerofill 0填充:未达到规定长度的数值,其数值前用0填充

Unsigned 无符号:即只有正数

*注意:这些约束可以同时使用多个,用********空格********隔开。*

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8jmzcmVB-1614847542298)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps317.jpg)]

*6.3、添加约束的时机*

\1. 创建表时

\2. 修改表时

*6.4、约束的添加分类*

\1. 列级约束:六大约束语法上都支持,但外键约束没有效果

\2. 表级约束:除了非空、默认,其他都支持

*6.5、建表时添加列级约束*

1、添加列级约束

Create table stuinfo(

Id int primary key,

Stuname varchar(20) not null,

Gender char(1)check(gender=‘男’ or gender=‘女’),

Seat int unique,

Age int default 20,

Majorid int foreign key references major(id)

);

Create table major(

Id int primary key,

Majorname carchar(20)

);

*6.6、建表时添加表级约束*

Create table stuinfo(

Id int ,

Stuname varchar(20),

Gender char(1),

Seat int ,

Age int,

Majorid int,

Constraint pk primary key(id),

Constraint uq unique(seat),

Constraint fk-stuinfo-major foreign key(majorid) references major(id)

);

*注意:********Constraint 别名 可以省略不写。*

7、*主键和唯一键的区别*

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JXclLx6P-1614847542299)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps318.jpg)]

允许组合的意思是:primary key(字段1,字段2)或者 unique(字段1,字段2)

这两个约束都是不能重复的,但是组合之后,如果插入的两个值只是字段1或者字段2的信息一样,是不会报错的,只有当字段1和字段2都插入重复的值才会报错。

8、*外键*

1、要求在从表中设置外键关系

2、从表的外键列的类型和主表的关联列的类型要求一致或兼容,名称无要求

3、主表的关联列必须是一个key(一般是主键或唯一键),如果不是一个key的话,是无法 建立外键约束的。

4、插入数据时,先插入主表,再插入从表;删除数据时,先删除从表,再删除主表。

9、*修改表*****时添加约束****

*9.1、添加非空约束*

Alter table 表名 modify column 字段名 字段类型 not null;

*9.2、添加默认约束*

Alter table 表名 modify column 字段名 字段类型 dafault (值);

*9.3、添加主键*

*9.3.1、列级约束的写法*

Alter table 表名 modify column 字段名 字段类型 primary key ;

*9.3.2、表级约束的写法*

Alter table 表名 ADD primary key(字段名);

*9.4、添加唯一约束*

*9.4.1、列级约束的写法*

Alter table 表名 modify column 字段名 字段类型 unique ;

*9.4.2、表级约束的写法*

Alter table 表名 ADD unique (字段名);

*9.5、添加外键*

Alter table 表名 add 【constraint 别名】 foreign key(从表的字段名) references 主表名(关联字段);

10、*修改表时删除约束*

*10.1、删除非空约束*

Alter table 表名 modify column 字段名 字段类型 【null】;

这里null不写也可以,因为要删除非空就是变为空,写了null就正好相反,或者不写,就是默认,默认也是null。

*10.2、删除默认约束*

Alter table 表名 modify column 字段名 字段类型 ;

*10.3、删除主键*

Alter table 表名 drop primary key ;

或者:

Alter table 表名 modify column 字段名 字段类型

*10.4、删除唯一键*

Alter table 表名 drop index 唯一键的字段 ;

唯一键的字段可以通过:show index from 表名 查出来,哪个是有唯一键的字段。

*10.5、删除外键*

Alter table 表名 drop foreign key 外键的别名 ;

11、*标识列*

标识列又称为自增长列,可以不用手动插入值,系统提供默认的序列值,从1开始

*11.1、创建表时设置标识列*

*1、语法:*

Create table 表名 (

字段名 字段类型 约束 aotu_increment,

字段名 字段类型 约束,

);

*2、例子:*

Create table test (

Id int primary key aotu_increment;

Name varchar(20)

);

*插入值*

Insert into test values(1,’john’);

如果这时候还要插入第二条值,Insert into test values(2,’lily’);这里的id这个值就要手动去改,因为id是主键,不能重复,我们需要手动去改,如果有很多数据的话,每次都要手动改就会很麻烦,这个时候就用到了标识列:aotu_increment。

当用到标识列的时候,一般是用在int类型上,我们添加数据的时候,就不能Insert into test values(1,’john’);这样去添加,应该Insert into test values(null,’john’);或者Insert into test values(’john’);这样去添加,这样的话,就会使自增的那个列,自动增加。

*注意:*

通过show variables like ‘%aotu_increment%’;来查看

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4Qld1pdq-1614847542299)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps319.jpg)]

第一行是偏移量,也就是每次增长多少

第二行是从哪个值开始自增

所以我们可以通过设置来实现需要的自增模式,但是mysql中不支持设置起始量,也就是刚开始的值肯定是1,只能设置偏移量:

Set aotu_increment_increment =3;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gFl7e0mE-1614847542299)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps320.jpg)]

前面说到了起始值是不能设置的,mysql不支持,但是也可以用另外一种方法来设置起始值

这是一种取巧的方法:

就是在最开始插入值的时候,Insert into test values(1,’john’),在id的位置,先自己设置一个值,这个值就是自己想要的起始值,然后再插入其他值,Insert into test values(null,’john’),这里的id的位置用null或者不写,这样就能实现改变起始值了。

*11.2、特点*

1、标识列必须和主键搭配吗?不一定,但要求必须是一个key,例如主键,唯一,外键,以后还能自己定义key。

2、一个表中至多只能有一个标识列

3、标识列的类型只能是数值型,int,float,double

4、标识列可以通过Set aotu_increment_increment =3;设置偏移量,也可以通过手动插入值 来改变起始值

*11.3、修改表时设置标识列*

语法:

Alter table 表名 modify column 字段名 字段类型 约束类型 aotu_increment;

*11.4、修改表时删除标识列*

语法:

Alter table 表名 modify column 字段名 字段类型 ;

*五、********数据库事务********(********TCL语言********)*

*1、********含义*

​ 通过一个或一组sql语句组成一个执行单元,这个执行单元要么全部执行,要么全部不执行。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-v9TLDGLw-1614847542300)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps321.jpg)]

*2、********特点********(********ACID********)*

​ ****原子性****:要么都执行,要么都不执行

​ ****一致性****:保证数据的状态操作前和操作后保持一致

​ ****隔离性****:多个事务同时操作相同数据库的同一个数据时,一个事务的执行不受其他 事务的干扰

​ ****持久性****:一个事务一旦提交,则数据将持久化到本地,除非其他事务对其进行修改

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CNY567U2-1614847542300)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps322.jpg)]

相关步骤:

​ 1、开启事务

​ 2、编写事务的一组逻辑操作单元(多条sql语句)

​ 3、提交事务或回滚事务

*3、********事务的分类*

*3.1、********隐式事务*

没有明显的开启和结束事务的标志

​ 比如

​ insert、update、delete语句本身就是一个事务

*3.2、********显式事务*

具有明显的开启和结束事务的标志

前提:必须先设置自动提交功能为禁用。

Show variables like ‘aotucommit’;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qsSYZtnH-1614847542301)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps323.jpg)]

Set aotucommit=off;

或者

Set aotucommit=0;

*3.3、步骤*

*3.3.1、开启事务*

Set aotucommit =0;

Start transaction;可选,可写可不写

*3.3.2、编写事务中的sql语句*

这里的语句一般就是:select、insert、update、delete

语句1;

语句2;

*3.3.3、结束事务*

1、确定事务没问题可以提交了:commit;

2、事务出现了问题,进行回滚:rollback;

*演示:*

Jhon和lily都有1000元,jhon向lily转了500元

*开启事务:*

Set aotucommit =0;

Start transaction;

*编写语句:*

Update test set money=500 where name=‘jhon’;

Update test set money=1500 where name =‘lily’;

*结束事务:*

Commit;

*3.4、savepoint的使用*

​ savepoint 节点名;设置保存点,只能搭配rollback使用。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9YUbiu12-1614847542302)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps324.jpg)]

这里有两条语句,删除id为25和28的数据,但是在这两条语句中间设置了一个保存 点a,最后rollback to a,就是回滚到a处,id为28的数据在保存点a之后,所以这条 数据不会被删除成功。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lU1Pf09o-1614847542303)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps325.jpg)]

*3.5、********事务的隔离级别*

*3.5.1、********事务并发问题如何发生*

当多个事务同时操作同一个数据库的相同数据时

*3.5.2、********事务的并发问题有哪些*

​ ****脏读:****一个事务读取到了另外一个事务未提交的数据

​ ****不可重复读:****同一个事务中,多次读取到的数据不一致

​ ****幻读:****一个事务读取数据时,另外一个事务进行更新,导致第一个事务读取到了没有更 新的数据

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-v5jBHUlJ-1614847542303)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps326.jpg)]

*3.5.3、********如何避免事务的并发问题*

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vuUJO1tc-1614847542304)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps327.jpg)]

*1、********通过设置事务的隔离级别********:*

​ 1、READ UNCOMMITTED 都不能避免

​ 2、READ COMMITTED 可以避免脏读

​ 3、REPEATABLE READ 可以避免脏读、不可重复读和一部分幻读

​ 4、SERIALIZABLE可以避免脏读、不可重复读和幻读

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Eqnyqzut-1614847542304)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps328.jpg)]

2、设置隔离级别:

​ set session(当前mysql的级别)| global(全局的级别) transaction isolation level 隔离级别名;

3、查看隔离级别:

​ select @@tx_isolation;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NcuWnJBN-1614847542304)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps329.jpg)]

*六、********视图*

*1、********含义*

理解成一张虚拟的表,和普通表一样使用

****例子:****查询姓张的学生名和专业名(学生名和专业名来自两张表)

以前:select stu-name,major-name

From students s

Inner join majors m

On s.majorid = m.id

Where s.stu-name like ‘张%’;

用视图以后:

先创建一个视图,将复杂的那一部分全部扔到视图里

Create view v1

As

select stu-name,major-name

From students s

Inner join majors m

On s.majorid = m.id

查询:

Select * from v1 where stu-name like ‘张%’;

*2、********视图和表的区别*

​ 使用方式 占用物理空间

视图 完全相同 不占用,仅仅保存的是sql逻辑

表 完全相同 占用

*3、********视图的好处*

​ 1、sql语句提高重用性,效率高

​ 2、和表实现了分离,提高了安全性

3、保护数据,提高安全性

*4、视图的操作*

*4.1、创建视图*

1、语法

Create view 视图名

As

查询语句;

2、例子

1.查询姓名中包含a字符的员工名、部门名和工种信息

①、创建视图

Create view v1

As

Select last-name,department-name,job-title

From employees e

Join departments d on e.department-id=d.department-id

Join jobs j on j.job-id = e.job-id;

②、使用①的结果

Select * from v1 where last-name like‘%a%’;

2.查询各部门的平均工资级别

①、创建视图查看每个部门的平均工资

Create view v2

As

Select avg(salary)ag ,department-id

From employees e

Group by department-id

②、使用①的结论

Select v2.ag,g.grade-level

From v2

Join jobs g

On v2.ag between g.heighest-sal and g.lowest-sal;

3.查询平均工资最低的部门信息

使用上面2的平均工资

Select* form v2 order by ag limit 1;

4.查询平均工资最低的部门名和工资

Create view v3

As

Select* form v2 order by ag limit 1;

Select d.*,v3.ag

From v3

Join departments d

On d.department-id=v3.department-id;

*4.2、********视图********数据的增删改查*

​ 1、查看视图的数据 ★

​ SELECT * FROM 视图名;

​ SELECT * FROM 视图名 条件语句;

​ 2、插入视图的数据

​ INSERT INTO 视图名(字段名1、字段名2,…) VALUES(对应的值…);

​ 对原始表也会进行更新,原始表里也会有插入的这条数据

​ 3、修改视图的数据

​ UPDATE 视图名 SET last_name =‘梦姑’ WHERE last_name=‘虚竹’;

​ 对原始表也可以更新

​ 4、删除视图的数据

​ DELETE FROM 视图名;

原始表里也会被删除

*注意:一般是不会对视图数据进行操作的,实际中,一般会给视图加权限,让视图只* *能读不能写,这样就不会对原始表产生影响,保证了安全性。*

*4.3、********某些视图不能********更新*

1、包含以下关键字的sql语句:分组函数、distinct、group by、having、union或者union all

2、常量视图

Create view v1

As

Select ‘john’ name;

Select后面就直接是一个常量的视图,就叫做常量视图。

Update v1 set name =‘lily’;

这是会报错的

3、Select中包含子查询

Create view v2

As

Select (select count(*)from employees);

这里用update更新也会报错,v2里面的数据不允许被更新

4、join

Create view v3

As

Select last-name,department-id

From employees e

Join departments d

On e.department-id=d.department-id;

这里也不能用update更新v3里面的数据

5、from一个不能更新的视图

Create view v4

As

Select * from v2

这个v2视图在上面是属于不能更新的视图,所以v4里面的数据也不能更新

6、where子句的子查询引用了from子句中的表

Create view v5

As

Select last-name,email

From employees e

Where employee-id in(

Select manager-id

From employees

Where manager-id is not null

);

v5里面的数据也不允许被更新

*4…4、********视图********的修改*

方式一:

​ CREATE OR REPLACE VIEW 视图名

​ AS

​ 查询语句;
​ 意思是如果视图存在则修改,如果不存在,则创建

​ 方式二:

​ ALTER VIEW视图名

​ AS

​ 查询语句;

*4.5、********视图的删除*

​ DROP VIEW 视图名1,视图名2,…;

*4.6、********视图结构的查看*

​ DESC 视图名;

​ SHOW CREATE VIEW视图名\G;

\G的作用就是将显示出来的结果格式化一下,便于观察

*七、变量*

*1、********系统变量*

变量由系统提供,不是用户定义的,属于服务器层面上的

*1.1、********全局变量*

作用域:服务器每次启动将为所有的全局变量赋初始值,针对于所有的会话(连接)有效, 但是不能夸重启

1、查看所有全局变量

​ SHOW GLOBAL VARIABLES;

2、查看满足条件的部分系统变量

​ SHOW GLOBAL VARIABLES LIKE ‘%char%’;

3、查看指定的系统变量的值

​ SELECT @@global.系统变量名;

不写默认是session

4、为某个系统变量赋值

​ 第一种:SET @@global.系统变量名=值;

​ 第二种:SET GLOBAL 系统变量名=值;

*注意:全局变量可以跨连接,在一个连接中设置好的全局变量,在打开几个连接,这个全* *局变量都是已经改变的值*

*1.2********、会话变量*

作用域:针对于当前会话(连接)有效,

1、查看所有会话变量

​ SHOW SESSION VARIABLES;

Session可以不写,默认就是session

2、查看满足条件的部分会话变量

​ SHOW SESSION VARIABLES LIKE ‘%char%’;

3、查看指定的会话变量的值

​ SELECT @@系统变量名;

​ SELECT @@session.系统变量名;

4、为某个会话变量赋值

​ 第一种:SET @@session.系统变量名=值;

​ 第二种:SET SESSION 系统变量名=值;

*2、********自定义变量*

变量是用户自定义的,不是系统提供的

步骤:声明、赋值、使用(查看,比较,运算)

*2.1、********用户变量*

作用域:针对于当前会话(连接)有效,同于会话变量的作用域

应用在任何地方,也就是放在begin end的里面或者外面

1、声明并初始化:

​ 第一种:SET @变量名=值;

​ 第二种:SET @变量名:=值;

​ 第三种:SELECT @变量名:=值;

2、赋值:

​ 方式一:一般用于赋简单的值

​ SET @变量名=值;

​ SET @变量名:=值;

​ SELECT @变量名:=值;

​ 方式二:一般用于赋表中的字段值

​ SELECT 字段名或表达式 INTO 变量名

​ FROM 表;

3、使用(查看用户变量的值):

​ select @用户变量名;

*2.2、********局部变量*

作用域:仅仅在定义它的begin end中有效

应用在begin end中的第一句话!!!

1、声明:

​ declare 变量名 类型 【default 值】;

2、赋值:

​ 方式一:一般用于赋简单的值

​ SET 变量名=值;

​ SET 变量名:=值;

​ SELECT @变量名:=值;

​ 方式二:一般用于赋表 中的字段值

​ SELECT 字段名或表达式 INTO 变量

​ FROM 表;

3、使用:

​ select 变量名

*3、********二者的区别*

​ 作用域 定义位置 语法

用户变量 当前会话 会话的任何地方 加@符号,不用指定类 型

局部变量 定义它的BEGIN END中 BEGIN END的第一句话 一般不用加@,需要指 定类型

*4、例子*

声明两个变量并赋初值,求和,并打印

*4.1、用用户变量实现*

Set @m=1;

Set @n=2;

Set @sum=@m+@n;

Select @sum;

*4.2、用局部变量实现*

Declare m int default 1;

Declare n int default 2;

Declare sum int;

Set sum=m+n;

Select sum;

*注意:一般来说这样写就好了,但是发现会报错,因为局部变量要在begin end中写,不能随便找个地方就写。*

*八、********存储过程********和函数*

类似于java中的方法

好处:

​ 1、提高了sql语句的重用性,减少了开发程序员的压力

​ 2、提高了效率,简化操作

*1、存储过程*

*1.1、含义*

一组经过预先编译的sql语句的集合,可以理解成批处理语句

*1.2、********好处*

​ 1、提高了sql语句的重用性,减少了开发程序员的压力

​ 2、简化操作

​ 3、减少了编译次数还减少了和数据库服务器连接的次数,提高了效率。

*1.3、********创建存储过程*

*1.3.1、********语法*

*1.3.1.1、创建语法*

​ create procedure 存储过程名(in|out|inout 参数名 参数类型,…)

​ begin

​ 存储过程体(一组合法的sql语句)

​ end

类似于java的方法:

​ 修饰符 返回类型 方法名(参数类型 参数名,…){

​ 方法体;

​ }

*1.3.1.2、调用语法*

Call 存储过程名(实参列表);

*1.3.2、********注意********点*

1、参数列表包含三部分

参数模式 参数名 参数类型

举例:in stuname varchar(20)

参数模式:

In:这个模式可以作为输入,也就是该参数需要调用方传入值

Out:这个模式可以作为输出,也就是该参数可以作为返回值

Inout:该参数既可以作为输入又可以作为输出,既需要传入值,又可以返回值

2、如果存储过程体仅仅只有一句话,begin end 可以省略

存储过程体中的每条sql语句必须加分号。

存储过程的结尾可以用delimeter这个关键字进行重新设置结束符。

语法:

​ delimiter 新的结束标记

​ 示例:

​ delimiter $

​ CREATE PROCEDURE 存储过程名(IN|OUT|INOUT 参数名 参数类型,…)

​ BEGIN

​ sql语句1;

​ sql语句2;

​ END $

*1.4、分类*

存储过程一般都是用于增删改,而且,一般都是涉及到大量的数据,几万,几十万,甚至几百万的数据。

*1.4.1、空参列表*

(以admin表为例)

select * from admin

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4TOQVrPv-1614847542306)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps330.jpg)]

插入5条数据

Delimeter $

Create procedure p1()

Begin

Insert into admin(username,password) values(‘john’,0000),(‘lily’,0000), (‘rose’,0000),(‘jack’,0000),(‘tom’,0000)

End $

*调用:*

Call p1()$

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nX23ImTJ-1614847542307)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps331.jpg)]

数据已插入

*1.4.2、带in模式参数的存储过程*

例子1:创建存储过程实现 根据女神名,查询对应的男神信息

Delimeter $

Create procedure p2(in beautyname varchar(20))

Begin

Select bo.*

From boys bo

Right join beauty b on bo.id = b.boyfriend-id

Where b.name = beautyname;

End $

调用:

Call p2(‘赵敏’)$

这里面直接写跟传入的参数 beantyname类型一致的即可。

例子2:创建存储过程,实现用户是否登录成功

Delimeter $

Create procedure p3(in username varchar(20),in password varchar(20))

Begin

Declare result int default 0; 声明并初始化

Select count() into result 将count()的值赋值给自定义变量result

From admin

Where admin.username=username

And admin.password=password;

Select if(result>0,‘成功’,‘失败’); 用if语句,将结果打印在界面上

End $

调用:

Call p3(‘john’,888888)$

*1.4.3、带out模式参数的存储过程*

例子1:根据女神名,返回对应的男神名

Delimeter $
create procedure p5(in beautyname varchar(20),out boyname varchar(20))

Begin

Select bo.boyname into boyname

From boys bo

Inner join beauty b on bo.id=b.boyfriend-id

Where b.name=beautyname

End $

调用:

方法一:

Set @bname //自定义一个变量,用于调用的时候放在输出值的位置

Call p5(‘赵敏’,@bname)$

方法二:也可以不自定义变量,直接使用即可

Call p5(‘赵敏’,@bname)$

Select @bname$

例子2:根据女神名,返回对应的男神名和男神魅力值(多个out可以一起使用)

Delimeter $

Create procedure p6(in beautyname varchar(20),out boyname varchar(20),out usercp int)

Begin

Select bo.boyname,bo.usercp into boyname,usercp

From boys bo

Inner join beauty b on bo.id=b.boyfriend-id

Where b.name=beautyname

End $

调用:

Call p6(‘赵敏’,@name,@usercp)$

Select @name,@usercp $

*1.4.4、带inout模式参数的存储过程*

例子:传入a,b两个值,最终a,b都翻倍并返回

Delimeter $

Create procedure p7(inout a int,inout b int)

Begin

Set a=a*2;

Set b=b*2;

End $

调用:

Set @m=10 $

Set @n=2 $

Call p7(@m,@n) $

Select @m,@n $

其实这里就是给a,b重新赋一下值,也就是给局部变量赋值,局部变量赋值的语法前面有说。

*注意:in、out、inout都可以在一个存储过程中带多个*

*1.5、删除存储过程*

语法:drop procedure 存储过程名;

一次只能删除一个存储过程

*1.6、查看存储过程的结构*

语法:Show create procedure 存储过程名;

存储过程是无法修改的,begin end里面的sql无法修改,想要改变,就只能删除存储过程重新再建立一个。

*2、********函数*

*2.1、含义*

一组经过预先编译的sql语句的集合,可以理解成批处理语句

*2.2、********好处*

​ 1、提高了sql语句的重用性,减少了开发程序员的压力

​ 2、简化操作

​ 3、减少了编译次数还减少了和数据库服务器连接的次数,提高了效率。

*2.3、********创建函数*

学过的函数:LENGTH、SUBSTR、CONCAT等

语法:

​ CREATE FUNCTION 函数名(参数名 参数类型,…) RETURNS 返回类型

​ BEGIN

​ 函数体

​ Return 值;

​ END

*注意:*

1、*参数列表包含两部分: 参数名 参数类型*

2、*函数体:肯定会有return语句,如果没有会报错,如果return语句没有放在函数体的最* *后也不报错,但不建议*

3、*函数体里面如果只有一个语句可以省略begin end*

4、*使用delimeter语句设置结束符*

*2.4、********调用函数*

​ SELECT 函数名(实参列表)

*2.5、例子*

*2.5.1、无参有返回*

返回公司的员工个数

Delimeter $

Create fuction f1() returns int

Begin

Declare c int default 0; //自定义一个变量c用于接收返回的员工个数

Select count(*)into c

From employees;

Return c;

End $

调用:

Select f1()$

*2.5.2、有参有返回*

例子1:根据员工名返回其工资

Delimeter $

Create function f2(empname varchar(20)) returns double

Begin

Set @sal =0;//自定义一个用户变量,也可以定义局部变量,都行

Select salary into @sal

From employees

Where last-name=empname;

Return @sal;

End $

调用:

Select f2(‘king’)$

例子2:根据部门名,返回该部门的平均工资

Delimeter $
create function f3(deptname varchar(20))returns double

Begin

Declare sal double;

Select avg(salary) into sal

From employees e

Inner join deparments d on e.department-id=d.department-id

Where d.depatment-name=deptname;

Return sal;

End $

调用:

Select f3(‘it’)$

*2.6、查看函数*

Show create function 函数名;

*2.7、删除函数*

Drop function 函数名;

*2.8、********函数和存储过程的区别*

​ 关键字 调用语法 返回值 应用场景

​ 函数 FUNCTION SELECT 函数() 只能是一个 一般用于查询结果为 一个值并返回时,当有 返回值而且仅仅一个

​ 存储过程 PROCEDURE CALL 存储过程() 可以有0个或多个 一般用于批量插 入,批量更新

*2.9、流程控制结构*

顺序结构:程序从上往下依次执行

分支结构:程序从两条或多条路径中选择一条去执行

循环结构:程序在满足条件的基础上,重复执行一段代码

*2.9.1、分支结构*

*2.9.1.1、********if函数*

实现简单的双分支

1、语法:

if(表达式1,表达式2,表达式3)

2、执行顺序:

如果表达式1成立,则返回表达式2的值,否则返回表达式3的值

3、应用:

任何地方

*2.9.1.2、********case语句*

1、语法:

​ ****情况一:****类似于java中的switch,一般用于实现等值判断

​ case 表达式|变量|字段

​ when 值1 then 结果1或语句1(如果是语句,需要加分号)

​ when 值2 then 结果2或语句2(如果是语句,需要加分号)

​ …

​ else 结果n或语句n(如果是语句,需要加分号)

​ end 【case】(如果是放在begin end中需要加上case,如果放在select后面不需要)

​ ****情况二:****类似于java中的多重if,一般用于实现区间判断

​ case

​ when 条件1 then 结果1或语句1(如果是语句,需要加分号)

​ when 条件2 then 结果2或语句2(如果是语句,需要加分号)

​ …

​ else 结果n或语句n(如果是语句,需要加分号)

​ end 【case】(如果是放在begin end中需要加上case,如果放在select后面不需要)

2、特点:

​ 可以作为表达式,嵌套在其他语句中使用,可以放在任何地方;

可以作为独立的语句去使用,如果作为独立的语句使用时,只能放在begin end中。

如果when中的值或条件成立,则执行对应的then后面的语句,并且结束case;如果 都不满足,则执行else中的语句或值。

Else可以省略,如果else省略了,并且所有when条件都不满足,则返回null。

*2.9.1.3、********if elseif语句*

实现多重分支

1、语法:

​ if 条件1 then 语句1;

​ elseif 情况2 then 语句2;

​ …

​ else 语句n;(else语句可省略)

​ end if;

2、特点:

​ 只能用在begin end中!!!!!!!!!!!!!!!

*2.9.2、********三者比较*

​ 应用场合

​ if函数 简单双分支

​ case结构 等值判断的多分支

​ if结构 区间判断的多分支

*2.9.3、********循环*

*2.9.3.1、分类*

While、loop、repeat

*2.9.3.2、循环控制*

Iterate类似于continue,继续,结束本次循环,继续下一次

Leave 类似于break,跳出,结束当前所在的循环,没有下一次

*2.9.3.3、while*

1、语法:

​ 【标签:】WHILE 循环条件 DO

​ 循环体;

​ END WHILE 【标签】;

*注意:写上标签,才能搭配上循环控制使用。例如有个条件是当i>=10的时候,就要跳出while循环(假设whlie循环的标签为a),在循环体里面就要进行判断,if i>=10 then leave a。*

*2.9.3.4、loop*

1、语法:

【标签:】loop

循环体;

End loop 【标签】;

*注意:这个没有循环条件,可以用来模拟死循环。*

*2.9.3.5、repeat*

1、语法:

【标签:】repeat

循环体;

Until 结束循环的条件

End repeat【标签】;

2、例子

1、批量插入,根据次数插入到admin表中多条记录

Delimeter $

Create procedure pro1(in insertcount int)

Begin

Declare i int default 1;

While i<=insercount do

Insert into admin(username,password) values(concat(‘jonh’,i),‘666’);

Set i=i+1;

End while;

End $

Call pro1(100)$

2、添加leave,批量插入,根据次数插入到admin表中多条记录,如果次数>20则停止

Delimeter $

Create procedure pro2(in insertcount int)

Begin

Declare i int default 1;

a:while i<=insertcount do

Insert into admin(username,password)values(concat(‘xiaohua’,i), ‘0000’);

If i>=20 then leave a;

End if;

Set i=i+1;

End while a;

End $

Call pro2(100)$

虽然我们传入的值是100,但是当次数到达20的时候,就会停止插入。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xoRmRsH0-1614847542308)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps332.jpg)]

3、添加iterate,批量插入,根据次数插入到admin表中多条记录,只插入偶数次

Delimeter $

Create procedure pro2(in insertcount int)

Begin

Declare i int default 0;

a:while i<=insertcount do

Set i=i+1;

If mod(i,2)!=0 then iterate a;

End if;

Insert into admin(username,password)values(concat(‘xiaohua’,i), ‘0000’);

End while a;

End $

Call pro2(100)$

*2.9.3.6、三者之间的比较*

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SpjM96UZ-1614847542310)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps333.jpg)]

九、Mysql高级

*1、索引*

*1.1、概念*

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hWnu9cbo-1614847542310)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps334.jpg)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xsAF8LPw-1614847542310)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps335.jpg)]

*1.2、索引的优势和劣势*

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eZdKUvkB-1614847542310)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps336.jpg)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PYdPFhdC-1614847542311)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps337.jpg)]

*1.3、索引的分类*

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ETM2ZK4y-1614847542311)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps338.jpg)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iJhtmFvd-1614847542313)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps339.jpg)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XFe0i4kt-1614847542313)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps340.jpg)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hgCbzPkX-1614847542314)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps341.jpg)]

*1.4、mysql索引结构*

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-d5pgYBvt-1614847542314)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps342.jpg)]

*1.4.1、B树的检索原理*

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zMygS15g-1614847542314)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps343.jpg)]

1、初始化介绍:

一颗B+树,浅蓝色的块我们称之为一个磁盘块,可以看到每个磁盘块包含几个数据项(深蓝色所示)和指针(黄色所示)。

如:磁盘块1包含数据项17,35,包含指针P1,P2,P3,

P1表示小于17的磁盘块,P2表示在17-35的磁盘块,P3表示大于35的磁盘块

*真实的数据存在于叶子节点即3,5,9,10,13,15,28,29,36,60,75,79,90,99*

*非叶子节点不存储真实的数据,只存储指引搜索方向的数据项,如17,35并不真实存在于数据表中*

2、查找过程

如果要查找数据项29,那么首先会把磁盘块1由磁盘加载到内存,此时发生一次IO,在内存中用二分查找确定29在17和35之间,锁定磁盘块1的P2指针,内存时间因为非常短(相比磁盘IO时间)可以忽略不计,通过磁盘块1的P2指针的磁盘地址把磁盘块3由磁盘加载到内存,发生第二次IO。29在26和30之间,锁定磁盘块3的P2指针,通过指针加载磁盘块8到内存,发生第三次IO。同时内存中做二分查找找到29,结束查询,总计三次IO。

第三层的B+树可以表示上百万的数据,如果上百万的数据查找只需要三次IO,性能提高是巨大的,如果没有索引,每个数据项都要发生一次IO,那么共需要百万次的IO,显然成本是非常高的。

*1.5、哪些情况需要创建索引*

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mVdDF3b0-1614847542314)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps344.jpg)]

*1.6、哪些情况不能创建索引*

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Mvp56qkV-1614847542315)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps345.jpg)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LwDwzkYX-1614847542315)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps346.jpg)]

*1.7、性能分析*

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WAEhkcbf-1614847542316)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps347.jpg)]

*1.7.1、Mysql Query Optimizer*

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-e5lMQjLS-1614847542316)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps348.jpg)]

*1.7.2、Mysql常见瓶颈*

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LrqhB6v5-1614847542316)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps349.jpg)]

*1.7.3、Explain*

*1.7.3.1、是什么(查看执行计划)*

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-B48ahgn7-1614847542317)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps350.jpg)]

*1.7.3.2、能干什么*

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-p2VxalSu-1614847542317)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps351.jpg)]

*1.7.3.3、怎么玩*

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OfRtniXe-1614847542318)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps352.jpg)]

*1.7.3.4、字段的含义*

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vicxJBa8-1614847542318)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps353.jpg)]

*1.7.3.4.1、Id*

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cYNUnwjY-1614847542319)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps354.jpg)]

Id相同:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-c4p0NRch-1614847542319)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps355.jpg)]

Id不同:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yv6vA9x9-1614847542320)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps356.jpg)]

就像一层层递归,想知道t2就必须先知道t1,想知道t1又必须先知道t3

Id相同不同,同时存在:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tZNDfYL8-1614847542320)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps357.jpg)]

*1.7.3.4.2、select-type*

1、类别

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-d5YeYJ5V-1614847542320)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps358.jpg)]

查询的类型,主要是用于区别普通查询,联合查询,子查询等复杂查询

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DRLFzDXE-1614847542321)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps359.jpg)]

*1.7.3.4.3、table*

显示这一行的数据是关于哪张表的

*1.7.3.4.4、type*

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-u2SEyzFl-1614847542322)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps360.jpg)]

访问类型排列:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PobUhu5p-1614847542322)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps361.jpg)]

*1.7.3.4.4.1、System*

表只有一行记录(等于系统表),这是constant(常量)类型的特例,平时不会出现,这个也可以忽略不计。在现实生活中,肯定会有大量的数据,不可能只有一行记录,这个只是理想中的最优。

*1.7.3.4.4.2、Const*

表示通过索引一次就找到了,const用于比较primary key或者unique索引。因为只匹配一行数据,所以很快。如:where后面的条件是一个主键,mysql就能将该查询转换为一个常量。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-d3zmgrYy-1614847542322)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps362.jpg)]

(select * from t1 where id=1)这里面的id=1就相当于一个常量,这个id是主键,具有唯一性,所以t1表的查询就是const,d1表的查询只有一张表一行数据,所以就是system

*1.7.3.4.4.3、eq-ref*

唯一性索引扫描,对于每一个索引键,表中只有一条数据与之匹配。常见于主键或唯一索引扫描(例如,要找一个公司的ceo,一个公司只有一个ceo)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-31eFhpyr-1614847542323)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps363.jpg)]

*1.7.3.4.4.4、Ref*

非唯一性索引扫描,返回匹配某个单独值的所有行,本质上也是一种索引访问,它返回所有匹配某个单独值的行,然而它可能会找到多个符合条件的行,所以他应该属于查找和扫描的混合体(例如,要找一个公司的程序员,一个公司可以有很多程序员)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0oFuzwk3-1614847542323)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps364.jpg)]

*1.7.3.4.4.5、range*

只检索给定范围的行,使用一个索引来选择行。Key列显示使用了哪个索引,一般就是在你的where语句中出现了between、<、>、in等查询,这种范围扫描索引比扫描全表要好,因为它只需要开始于索引的某一点,而结束于另一点,不用扫描全部索引。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-d5i1wbg7-1614847542323)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps365.jpg)]

*1.7.3.4.4.6、index*

Full index scan,index与all区别为index类型只遍历索引树。这通常比all快,因为索引文件通常比数据文件小。(****也就是说虽然all和index都是读全表****,但是index是从索引中读取出的,而all是从硬盘中读的)。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oeDBa0QW-1614847542324)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps366.jpg)]

*1.7.3.4.4.7、all*

全表扫描

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7oUsXGbr-1614847542325)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps367.jpg)]

*1.7.3.4.5、possible-keys*

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZlhcPhyc-1614847542326)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps368.jpg)]

*1.7.3.4.6、key*

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ub7Fa2PW-1614847542326)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps369.jpg)]

*上图中 应该是复合索引,而不是覆盖索引*

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pLRVlDT3-1614847542327)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps370.jpg)]

t2表中的col1和col2字段被作为一个复合索引名为idx_col1_col2,我们查询的字段是col1,col2,这刚好是一个索引,所以可以直接在索引树里面进行完成,它既然是在索引树里面完成的,那使用到的索引一定就是该索引树。

*1.7.3.4.7、key_len*

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7BX8H8sL-1614847542328)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps371.jpg)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uxB6d3Y6-1614847542328)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps372.jpg)]

查询条件越精确,越多,key_len就会越长

*1.7.3.4.8、ref*

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2V00FTI6-1614847542328)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps373.jpg)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-x8RN5OXX-1614847542330)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps374.jpg)]

*1.7.3.4.9、rows*

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-j1MGEvqU-1614847542330)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps375.jpg)]

没建索引之前

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sEQmPpeP-1614847542331)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps376.jpg)]

建立索引之后

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Z2KoBLH1-1614847542331)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps377.jpg)]

*1.7.3.4.10、extra*

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-K6qm31fP-1614847542331)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps378.jpg)]

*1.7.3.4.10.1、Using filesort(最好不要出现)*

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hdodZ0Dm-1614847542332)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps379.jpg)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nWSTMiju-1614847542333)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps380.jpg)]

这里创建了一个复合索引,把字段col1,2,3都整合为索引,前者的explain语句后面order by的条件只用到了col3,整个语句中只用到了col1和col3,然而复合索引是col1,2,3,所以在extra里面出现了using filesort,这是因为没有安全索引的要求来;而后者,explian语句完整的包括了col1,2,3,所以在extra里面并没有出现using filesort。在实际应用中,应该尽量避免using filesort的出现。

*1.7.3.4.10.2、using temporary(最好不要出现)*

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8NpP8rKW-1614847542333)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps381.jpg)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XtdK19Sx-1614847542333)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps382.jpg)]

*总结:当使用到索引,也使用到group by的时候,group by后面一定要跟索引的个数和顺序一样,否则很容易出现临时表和文件排序,而这两个都是我们不希望出现的。*

*1.7.3.4.10.3、using index(最好要出现)*

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ppOBNk0A-1614847542333)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps383.jpg)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uWAWAsec-1614847542334)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps384.jpg)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-U6KBIsBZ-1614847542334)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps385.jpg)]

覆盖索引的意思就是,我用了col1,col2,col3作为创建复合索引的字段,那最后查询的时候,所查询的字段也正好是col1,col2,col3。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fiIC0IuI-1614847542335)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps386.jpg)]

*1.7.3.4.10.4、其他不重要的*

1、using where

表明使用了过滤

2、using jion buffer

使用了连接缓存

3、impossible where

Where子句的值总是false,不能用来获取任何元组

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iB8izUeR-1614847542335)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps387.jpg)]

一个人的名字不可能同时为两个,所以提示,这个where是不可能的where

4、select tables optimized away

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zBWcl5HA-1614847542335)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps388.jpg)]

5、Distinct

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VS0Y4RKU-1614847542336)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps389.jpg)]

*1.8、索引优化*

*1.8.1、索引分析*

索引最好建立在要经常查询的字段中

如果是单表查询,直接建立一个索引即可

如果是两表连接的查询,那左连接就把索引建在右边的表中

右连接就把索引建在左边的表中。

*1.8.2、索引失效(要避免)*

*索引失效的原因:*

*1.8.2.1、全值匹配*

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mM64ErMB-1614847542337)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps390.jpg)]

只要建立复合索引时,例如name,age,pos这几个字段按顺序建立一个复合索引,在explain的时候,在where条件语句中把name写在第一位,并且按顺序写条件,就不会失效,可以参考下面的第二条。

*1.8.2.2、最佳左前缀*

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uf2W79HJ-1614847542337)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps391.jpg)]

如果我们建立一个按顺序的复合索引name,age,pos,然后第一个例子我们的条件只有age和pos,可以看到索引已经失效了,而且进行了全表扫描,这是我们应该要避免的

同理看第二个例子,我们只查询pos字段,虽然这个字段也在索引里面,但是他不是作为索引的第一个,索引这样查询的时候,也是会进行全表扫描,并且索引失效

*总结:当建立了复合索引的时候,在查询条件语句中(where语句),一定要把位于第一个的字段也放在里面,也就是这里的name。*

*如果说我们建立索引的时候,是按age,name,pos这样的顺序排列的,那在查询条件语句中,我们就必须把age写在第一位。也就是说,哪个字段放在建立复合索引的第一个位置,那么那个字段名就必须出现在where语句中。*

注意:这就相当于是一辆火车,位于复合索引第一个位置的就是火车头,火车头不能没有,后面的字段相当于火车厢,火车厢可以有很多节,但要注意的是,火车厢一定是按顺序一节一节的,不能说中间断了一节或者几节,不然火车一样开不了,就像下面这样:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NczX69Q1-1614847542338)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps392.jpg)]

这里的where条件是name和pos,但是按照顺序的话应该是name,age,pos,可以看到这里虽然使用到了索引,但是这只是一个假象,再仔细看key_len,如果是name和pos都使用到了索引,那在key_len这里应该是大于74的,之前我们只查询name的时候,key_len就是74,当我们查询得越精确,这个长度会越大,而这里只有74,如果这个证据不够明显,再看ref,这里只有一个const,我们的where语句中是有两个“=”的,前后用and连接,说明这里有两个常量,正常来说,这里应该是两个const才对,由此可见,这里虽然使用到了索引,但是这个索引只是name使用的索引,后面的pos并没有使用到索引,这里还是属于索引失效。这也正说明了,我们的顺序是name,age,pos,当我们越过中间的age,也就是中间的火车厢,那后面的火车厢就断了,无法继续行驶。

*正常情况下的查询两个字段*

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QvtWgFPL-1614847542338)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps393.jpg)]

*1.8.2.3、不在索引列上做任何操作*

计算、函数、(自动or手动)类型转换),会导致索引失效而转向全表扫描

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NRcNlITz-1614847542338)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps394.jpg)]

第一次explain的时候是正常使用到了索引的name,所以查询也是很不错的

第二次在name这个索引外面加了个条件,left是个函数,所以查询出来是很垃圾的

如果不用explain就这样正常去查询,select * from staffs where name=‘july’和

Select * from staffs where left(name,4)=’july’都是可以查询出july的,但是当使用explain查询性能的时候,就可以很清楚的看到如果在索引列上加了操作,就会使索引失效

*1.8.2.4、********存储引擎不能使用索引中范围条件右边的列*

范围 若有索引则能使用到索引,范围条件右边的索引会失效(范围条件右边与范围条件使用的同一个组合索引,右边的才会失效。若是不同索引则不会失效)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TO7XpkmK-1614847542339)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps395.jpg)]

可以看到前面三个都能正常查询,但是最后一个,当索引列变成了一个范围,这个时候的type就变成了range,说明name这个索引列用到了,age这个索引列也用到了,也可以从key_len这个值来看,是78,说明也是用到了两个索引列,但是,很明显,age后面的那个pos就索引失效了。而且这里的name和age这两个索引列是不一样的,name是正儿八经的查找,而age是一个范围,就变成了排序。

*1.8.2.5、尽量使用覆盖索引*

只访问索引的查询(索引列和查询列一致)),减少select *

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ooriPQ0r-1614847542340)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps396.jpg)]

当索引列和查询列一致时,extra会出现使用索引的提示,这是很好的。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XBhxxhw7-1614847542340)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps397.jpg)]

第一个:

当age是一个范围的时候,key_len就变成了74,说明age这个索引并没有被使用到,这里的type已经变成了ref,不再是range,而age>25是一个range的范围,ref的优先级比range高,所以这里age这个索引列并不能被使用到。

第二个:

这里的age是一个常量,所以可以看到,这里的key_len是78,ref也是两个const,说明这里的age索引列是被用到了

第三个:

同理第二个

*1.8.2.6、mysql********在使用********(!=* *或者********<>)********的时候无法使用索引*

索引 idx_nameAgeJob

​ idx_name

使用 != 和 <> 的字段索引失效( != 针对数值类型。 <> 针对字符类型

前提 where and 后的字段在混合索引中的位置比当前字段靠后 where age != 10 and name=‘xxx’ ,这种情况下,mysql自动优化,将 name=‘xxx’ 放在 age !=10 之前,name 依然能使用索引。只是 age 的索引失效)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3T2zgapb-1614847542340)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps398.png)]

*1.8.2.7、********is not null* *也无法使用索引********,********但是********is null********是可以使用索引的*

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-z9ivwYHz-1614847542341)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps399.jpg)]

*1.8.2.8、********like********以通配符开头********(’%abc…’)mysql********索引失效*

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uloDvyZK-1614847542341)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps400.jpg)]

使用的like关键字的时候,如果%通配符出现在最前面,就会索引失效,引发全表扫描。

like ‘%abc%’ type 类型会变成 all

like ‘abc%’ type 类型为 range ,算是范围,可以使用索引

注意:在实际生产环境中,有可能必须使用到%字符串%这种形式

例如:我们用不会让索引失效的方法去查询,连结果都查询不到,这样是没有意义的。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AEmtW8Po-1614847542341)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps401.jpg)]

使用%字符串%可以让索引不失效的办法:

CREATE TABLE tbl_user (

id INT(11) NOT NULL AUTO_INCREMENT,

NAME VARCHAR(20) DEFAULT NULL,

age INT(11) DEFAULT NULL,

email VARCHAR(20) DEFAULT NULL,

PRIMARY KEY (id)

) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

*这张表里一共有四个字段,分别是id,name,age,email*

CREATE INDEX idx_user_nameAge ON tbl_user(NAME,age);

*创建一个复合索引,将字段name,age作为索引列*

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8bJRxkGO-1614847542342)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps402.jpg)]

可以看到,这里即使使用了%字符串%,只要查询列表是索引列表或者索引列表的一部分,索引都不会失效。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eQwOoVGV-1614847542343)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps403.jpg)]

因为id是自增的主键,也是一个索引,所以就算select后面有id字段,也是不会使索引失效的。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bzLxI5Nr-1614847542344)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps404.jpg)]

但是如果在查询列表中加入了不是索引列的字段,就会让索引失效。

*1.8.2.9、字符串不加单引号索引失效*

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-i3kJLw0c-1614847542345)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps405.jpg)]

这里只是单纯地查找而已,name是一个varchar类型,在mysql中字符串加不加单引号都能查出来,建议要加。

但是如果加了explain就不一样了:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OKPgbjg3-1614847542345)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps406.jpg)]

很明显,后面因为字符串name没有加单引号,所以索引失效了。这也就是1.8.2.3所说的,不要在索引列上做任何操作,自动或者手动进行数据转换也不可以。在mysql中,数值和字符可以自动转换。

*1.8.2.10、少用or*

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XrGzlDSb-1614847542346)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps407.png)]

可以正常查询,但是性能不高

*1.8.3、总结*

假设index(a,b,c)

*Where语句* *索引是否被使用*
where a = 3 Y,使用到a
where a = 3 and b = 5 Y,使用到a,b
where a = 3 and b = 5 and c = 4 Y,使用到a,b,c
where b = 3 或者 where b = 3 and c = 4 或者 where c = 4 N
where a = 3 and c = 5 使用到a, 但是c不可以,b中间断了
where a = 3 and b > 4 and c = 5 使用到a和b, c不能用在范围之后,b后断了
where a = 3 and b like ‘kk%’ and c = 4 Y,使用到a,b,c
where a = 3 and b like ‘%kk’ and c = 4 Y,只用到a
where a = 3 and b like ‘%kk%’ and c = 4 Y,只用到a
where a = 3 and b like ‘k%kk%’ and c = 4 Y,使用到a,b,c

*1.9、注意事项*

【建表语句】

create table test03(

id int primary key not null auto_increment,

c1 char(10),

c2 char(10),

c3 char(10),

c4 char(10),

c5 char(10)

);

insert into test03(c1,c2,c3,c4,c5) values(‘a1’,‘a2’,‘a3’,‘a4’,‘a5’);

insert into test03(c1,c2,c3,c4,c5) values(‘b1’,‘b2’,‘b3’,‘b4’,‘b5’);

insert into test03(c1,c2,c3,c4,c5) values(‘c1’,‘c2’,‘c3’,‘c4’,‘c5’);

insert into test03(c1,c2,c3,c4,c5) values(‘d1’,‘d2’,‘d3’,‘d4’,‘d5’);

insert into test03(c1,c2,c3,c4,c5) values(‘e1’,‘e2’,‘e3’,‘e4’,‘e5’);

select * from test03;

【建索引】

create index idx_test03_c1234 on test03(c1,c2,c3,c4);

show index from test03;

*1.9.1、注意一*

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AjNPRPoc-1614847542346)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps408.jpg)]

这里的顺序和创建索引列的顺序不一样,但是一样使用到了索引,而且是全部索引,这是因为,mysql会自动在内部进行调优。之前说的一定要按顺序来,其实指的是,中间的某一个索引列不能断,就是所有的索引列必须都写上,哪怕创建索引的时候处于第一个位置的索引列在where条件中不是处于第一个位置,只要把所有的索引列都写在where语句中,mysql就会自动进行调优,索引不会失效。

*总之就是一句话:*

*如果索引列全部都写上去了,就不用关心其顺序,mysql会自动进行调优,但是如果索引列没有全部写上去,按顺序从c1,c2,c3,c4这样写,只要是按顺序的,中间没有少,(c1,c2或者c1,c2,c3)这样索引也不会失效,但是如果是(c1,c3,c4)中间少了个c2,这样就只有c1用到了索引,c3,c4的索引就失效了。*

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-n0lQ6fZP-1614847542347)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps409.jpg)]

*1.9.2、注意二*

explain select * from test03 where c1=‘a1’ and c2=‘a2’ and c3>‘a3’ and c4=‘a4’;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SFeYnTWk-1614847542347)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps410.jpg)]

这里的c3是个范围,所以在c3这里就是范围之后全失效,c4也就用不到索引了。

*1.9.3、注意三*

explain select * from test03 where c1=‘a1’ and c2=‘a2’ and c4>‘a4’ and c3=‘a3’;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BtlM7Ex3-1614847542347)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps411.jpg)]

这里跟注意二是相似的,但是有一点不同的是,这里把所有的索引列全部都写上了,所以mysql会自动进行调优,它会把c3=‘a3’放到c4>‘a4’的前面,而c4因为是个范围,所以范围之后全失效,但是c4后面已经没有东西了,所以无所谓。所以其实所有的索引列都用到了索引。

*1.9.4、注意四*

explain select * from test03 where c1=‘a1’ and c2=‘a2’ and c4=‘a4’ order by c3;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-j4FOagZf-1614847542348)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps412.jpg)]

这里虽然把所有的索引列都写上了,但是可以看到key_len是22,说明只有c1和c2用到了索引,c4因为中间断了个c3,所以没用到。

*但是严格意义上来说,c3也是用到了索引,索引的作用有两个,一个是查找,一个是排序,这里的c3其实就是在排序。*

*1.9.5、注意五*

explain select * from test03 where c1=‘a1’ and c2=‘a2’ order by c3;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sdwkTVng-1614847542349)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps413.jpg)]

跟注意四一样,其实这里有没有c4都一样,因为oder by c3跟and c3不一样,mysql不能进行自动调优,所以在c2的后面,就已经断了,所以这个结果会跟注意四的结果一样。

*1.9.6、注意六*

explain select * from test03 where c1=‘a1’ and c2=‘a2’ order by c4;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WsLOjlCl-1614847542349)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps414.jpg)]

这里c1,c2用到了索引,但是没有c3,所以就只有两个用到了索引,但是这里还出现了filesort,这个是我们要尽量避免的,因为我们索引列是c1,c2,c3,c4,这里c3不见了,直接跳到c4,mysql只能自己内部进行排序,就出现了filesort,这是很耗性能的。

*1.9.7、注意七*

*1.9.7.1、* *无filesort*

explain select * from test03 where c1=‘a1’ and c5=‘a5’ order by c2,c3;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lVfRZ6v3-1614847542349)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps415.jpg)]

只用c1一个字段索引,但是c2、c3用于排序,

*1.9.7.2、有********filesort*

explain select * from test03 where c1=‘a1’ and c5=‘a5’ order by c3,c2;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QGxinr1g-1614847542350)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps416.jpg)]

我们建的索引是c1,c2,c3,c4,它没有按照顺序来,c3,c2 颠倒了

*对比一下:*

explain select * from test03 where c1=‘a1’ and c2=‘a2’ order by c2,c3;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Qp68aCMV-1614847542352)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps417.jpg)]

两者没有任何差别,因为在c2=‘a2’后面,跟的不是c3=‘a3’而是c5=‘a5’,中间断了个c3和c4,所以c5是索引失效的。

*1.9.8、注意八*

explain select * from test03 where c1=‘a1’ and c2=‘a2’ and c5=‘a5’ order by c2,c3;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gYue2xHP-1614847542352)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps418.jpg)]

这里c1=‘a1’,c2=‘a2’都存在了,c1,c2是正常索引,所以后面的order by c2,c3也可以正常进行排序,并没有断掉中间的一个,所以没有filesort

*对比一下:*

explain select * from test03 where c1=‘a1’ and c2=‘a2’ and c5=‘a5’ order by c3,c2;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OAofl8V1-1614847542352)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps419.jpg)]

这里和上面的1.9.7.2不一样,在1.9.7.2里面,where后面只有c1=‘a1’,后面就是c5=‘c5’,然后后面order by的时候,是c3在前,c2在后,这样的话,是顺序颠倒的,所以会出现filesort

但是这里where后面是c1=‘a1’,c2=‘a2’,这里已经用到了c1,c2两个索引了,所以后面order by的顺序即使是c3在前,c2在后,因为c2的索引已经使用到了,c2就变成了一个固定的常量,所以后面的order by 就变成了order by c3,常量(c2),所以不会出现filesort。

*1.9.9、注意九*

explain select * from test03 where c1=‘a1’ and c4=‘a4’ group by c2,c3;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8VXPxcKq-1614847542353)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps420.jpg)]

*对比一下:*

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qCqBjbIe-1614847542353)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps421.jpg)]

前面的是c2,c3的顺序,所以没有异常,后面的顺序是c3,c2,不仅出现了filesort,还出现了temporary。

*2、查询截取分析*

*2.1、查询优化*

*2.1.1、小表驱动大表*

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3JA2315d-1614847542354)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps422.jpg)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9fudjmy2-1614847542355)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps423.jpg)]

*实验:*

1、有索引 大表驱动小表

select sql_no_cache sum(sal) from emp where deptno in (select deptno from dept);

select sql_no_cache sum(sal) from emp where exists (select 1 from dept where emp.deptno=dept.deptno); ##用 exists 是否存在,存在返回一条记录,exists 是作为一个查询判断用,所以 select 后返回什么不重要。

select sql_no_cache sum(sal) from emp inner join dept on emp.deptno=dept.deptno;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bK5Rm0WM-1614847542355)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps424.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OgjDKz7k-1614847542355)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps425.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QwDkoo3Y-1614847542356)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps426.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jmzpGgCF-1614847542357)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps427.png)]

2、有索引 小表驱动大表

select sql_no_cache sum(e.sal) from (select * from emp where id<10000) e where exists (select 1 from emp where e.deptno=emp.deptno);

select sql_no_cache sum(e.sal) from (select * from emp where id<10000) e inner join (select distinct deptno from emp) m on m.deptno=e.deptno;

select sql_no_cache sum(sal) from emp where deptno in (select deptno from dept);

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kCqmC9Az-1614847542357)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps428.png)]有索引小表驱动大表 性能优于 大表驱动小表

3、无索引 小表驱动大表

select sql_no_cache sum(e.sal) from (select * from emp where id<10000) e where exists (select 1 from emp where e.deptno=emp.deptno);

select sql_no_cache sum(e.sal) from (select * from emp where id<10000) e inner join (select distinct deptno from emp) m on m.deptno=e.deptno;

select sql_no_cache sum(sal) from emp where deptno in (select deptno from dept);

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3pi7ijC1-1614847542358)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps429.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xizlwgRQ-1614847542358)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps430.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zmEu8d0K-1614847542358)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps431.png)]

4、无索引大表驱动小表

select sql_no_cache sum(sal) from emp where deptno in (select deptno from dept);

select sql_no_cache sum(sal) from emp where exists (select 1 from dept where emp.deptno=dept.deptno);

select sql_no_cache sum(sal) from emp inner join dept on emp.deptno=dept.deptno;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FI0OPEC6-1614847542358)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps432.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BwJppYrT-1614847542359)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps433.png)]

*结论:*

*有索引的情况下*

用 inner join 是最好的 其次是 in ,exists最糟糕

*无索引的情况下小表驱动大表*

因为join 方式需要distinct ,没有索引distinct消耗性能较大

所以 exists性能最佳 in其次 join性能最差?

*无索引的情况下大表驱动小表*

in 和 exists 的性能应该是接近的 都比较糟糕 exists稍微好一点 超不过5% 但是inner join 优于使用了 join buffer 所以快很多

如果left join 则最慢

*2.1.2、order by关键字优化(关注filesort)*

ORDER BY子句,尽量使用Index方式排序,避免使用FileSort方式排序

*2.1.2.1、建表*

CREATE TABLE tblA(

id int primary key not null auto_increment,

age INT,

birth TIMESTAMP NOT NULL,

name varchar(200)

);

INSERT INTO tblA(age,birth,name) VALUES(22,NOW(),‘abc’);

INSERT INTO tblA(age,birth,name) VALUES(23,NOW(),‘bcd’);

INSERT INTO tblA(age,birth,name) VALUES(24,NOW(),‘def’);

创建索引

CREATE INDEX idx_A_ageBirth ON tblA(age,birth,name);

查看表

SELECT * FROM tblA;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Uz5pMtxT-1614847542360)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps434.jpg)]

*2.1.2.2、案例*

1、explain SELECT * FROM tblA where age>20 order by age;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ypVoqspx-1614847542360)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps435.jpg)]

我们建立的索引是age,birth,name三者都有,这里age在第一个位置,火车头有了,而且age是在索引上,所以不会出现filesort。

2、explain SELECT * FROM tblA where age>20 order by age,birth;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XfUVkqrR-1614847542361)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps436.jpg)]

同理,这里也不会出现filesort

3、explain SELECT * FROM tblA where age>20 order by birth;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lPh912GN-1614847542361)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps437.jpg)]

这里出现了filesort,是因为where后面并不是一个常量,而且,如果想不出现filesort,那在order by 后面也应该按照创建索引列的顺序去写,如果顺序颠倒了,就会出现filesort。

4、explain SELECT * FROM tblA where age>20 order by birth,age;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eBw7HFvN-1614847542361)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps438.jpg)]

与3同理

5、explain SELECT * FROM tblA where age=20 order by birth,age;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PxsdWaCR-1614847542362)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps439.jpg)]

这里的age是一个常量,所以order by后面就相当于 order by birth,常量(age),所以不会出现filesort。

6、explain SELECT * FROM tblA order by birth;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aPx2RcQT-1614847542362)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps440.jpg)]

创建索引的时候是age,birth,name,这里连火车头age都没有,直接就按照birth排序,mysql只能内部进行排序,就出现了filesort。

7、explain SELECT * FROM tblA where birth > '2020-09-02 00:00:00’order by birth;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GTSat6oe-1614847542363)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps441.jpg)]

与6同理

8、explain SELECT * FROM tblA where birth > '2020-09-02 00:00:00’order by age;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kSenCRAC-1614847542363)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps442.jpg)]

Filesort出现的原因是因为排序,如果有跳过中间的一个给后面的一个排序,例如(where c1=a1 order by c3)这样就会出现filesort,或者将要排序的索引列顺序颠倒,例如(where c1=a1 order by c3,c2),这样也会出现filesort,但是这里的order by 后面就是age,就是索引列的第一个,所以不会出现filesort。

9、explain SELECT * FROM tblA order by age asc,birth desc;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qgD9RcXr-1614847542364)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps443.jpg)]

我们在创建索引的时候,是自动按照升序来排的,也就是说age,birth都是按照升序排列的,但是当我们把birth按照降序排了之后,原来的升序就用不到了,所以mysql只能内部进行排序就出现了filesort。

*与下面进行对比:*

explain SELECT * FROM tblA order by age asc,birth asc;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Iv3zC0UY-1614847542364)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps444.jpg)]

这里两个都是升序,就跟原来的默认升序一样,所以不会出现filesort。

*2.1.2.3、结论*

MySQL支持二种方式的排序,FileSort和Index,Index效率高,它指MySQL扫描索引本身完成排序。FileSort方式效率较低。

1、ORDER BY满足两情况,会使用Index方式排序:

ORDER BY 语句使用索引最左前列

使用Where子句与Order BY子句条件列组合满足索引最左前列

where子句中如果出现索引的范围查询(即explain中出现range)会导致order by 索引失效。

2、尽可能在索引列上完成排序操作,遵照索引建的最佳左前缀

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Rvi729sA-1614847542365)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps445.png)]

第二种中,where a = const and b > const order by b , c 不会出现 using filesort b , c 两个衔接上了

但是:where a = const and b > const order by c 将会出现 using filesort 。因为 b 用了范围索引,断了。而上一个 order by 后的b 用到了索引,所以能衔接上 c

3、如果不在索引列上,filesort有两种算法:mysql就要启动双路排序和单路排序

*双路排序:*

MySQL 4.1之前是使用双路排序,字面意思就是两次扫描磁盘,最终得到数据,读取行指针和orderby列,对他们进行排序,然后扫描已经排序好的列表,按照列表中的值重新从列表中读取对应的数据输出。

从磁盘取排序字段,在buffer进行排序,再从磁盘取其他字段。

(多路排序需要借助 磁盘来进行排序。所以 取数据,排好了取数据。两次 io操作。比较慢

单路排序 ,将排好的数据存在内存中,省去了一次 io 操作,所以比较快,但是需要内存空间足够。)

在mysql4.1之后,出现了第二种改进的算法,就是****单路排序:****

从磁盘读取查询需要的所有列,按照order by列在buffer对它们进行排序,然后扫描排序后的列表进行输出,它的效率更快一些,避免了第二次读取数据。并且把随机IO变成了顺序IO,但是它会使用更多的空间,因为它把每一行都保存在内存中了。

单路虽然比多路要好,但是单路也存在一些问题:

在sort_buffer中,方法B比方法A要多占用很多空间,因为方法B是把所有字段都取出, 所以有可能取出的数据的总大小超出了sort_buffer的容量,导致每次只能取sort_buffer容量大小的数据,进行排序(创建tmp文件,多路合并),排完再取取sort_buffer容量大小,再排……从而多次I/O。

本来想省一次I/O操作,反而导致了大量的I/O操作,反而得不偿失。

*解决办法:*

1、增大sort_buffer_size参数的设置(用于单路排序的内存大小)

2、增大max_length_for_sort_data参数的设置(单次排序字段大小。(单次排序请求))

3、去掉select 后面不需要的字段(select 后的多了,排序的时候也会带着一起,很占内存,所以去掉没有用的)

*提高Order By的速度*

\1. Order by时select * 是一个大忌只Query需要的字段, 这点非常重要。在这里的影响是:

1.1 当Query的字段大小总和小于max_length_for_sort_data 而且排序字段不是 	TEXT|BLOB 类型时,会用改进后的算法——单路排序, 否则用老算法——多路排序。

1.2 两种算法的数据都有可能超出sort_buffer的容量,超出之后,会创建tmp文件进行	合并排序,导致多次I/O,但是用单路排序算法的风险会更大一些,所以要提高		sort_buffer_size。

\2. 尝试提高 sort_buffer_size

不管用哪种算法,提高这个参数都会提高效率,当然,要根据系统的能力去提高,因为 这个参数是针对每个进程的

\3. 尝试提高 max_length_for_sort_data

提高这个参数, 会增加用改进算法的概率。但是如果设的太高,数据总容量超出 sort_buffer_size的概率就增大,明显症状是高的磁盘I/O活动和低的处理器使用率.

*2.1.2.4、分页查询的优化–limit*

EXPLAIN SELECT SQL_NO_CACHE * FROM emp ORDER BY deptno LIMIT 10000,40

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-krz2AqEE-1614847542365)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps446.png)]

那我们就给deptno这个字段加上索引吧。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-s69ZDr2W-1614847542365)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps447.png)]

然并卵。

优化: 先利用覆盖索引把要取的数据行的主键取到,然后再用这个主键列与数据表做关联:(查询的数据量小了后)

EXPLAIN SELECT SQL_NO_CACHE * FROM emp INNER JOIN (SELECT id FROM emp e ORDER BY deptno LIMIT 10000,40) a ON a.id=emp.id

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2m17OJZ6-1614847542366)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps448.png)]

最后比较一下查询速度:

优化前:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iSYPIjlI-1614847542366)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps449.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Wq7tQcFO-1614847542366)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps450.png)]

优化后:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-81wyviOR-1614847542367)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps451.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YmWQGR1E-1614847542367)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps452.png)]

实践证明: ①、order by 后的字段(XXX)有索引 ②、sql 中有 limit 时,

当 select id 或 XXX字段索引包含字段时 ,显示 using index

当 select 后的字段含有 order by 字段索引不包含的字段时,将显示 using filesort

*2.1.4.5、group by优化*

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Rt5Nko10-1614847542367)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps453.jpg)]

*2.2、慢查询日志*

*2.2.1、什么是慢查询日志*

MySQL的慢查询日志是MySQL提供的一种日志记录,它用来记录在MySQL中响应时间超过阀值的语句,具体指运行时间超过long_query_time值的SQL,则会被记录到慢查询日志中。

具体指运行时间超过long_query_time值的SQL,则会被记录到慢查询日志中。long_query_time的默认值为10,意思是运行10秒以上的语句。

由他来查看哪些SQL超出了我们的最大忍耐时间值,比如一条sql执行超过5秒钟,我们就算慢SQL,希望能收集超过5秒的sql,结合之前explain进行全面分析。

*2.2.2、如何使用*

*2.2.2.1、前期说明*

默认情况下,MySQL数据库没有开启慢查询日志,需要我们手动来设置这个参数。

当然,如果不是调优需要的话,一般不建议启动该参数,因为开启慢查询日志会或多或少带来一定的性能影响。慢查询日志支持将日志记录写入文件

*2.2.2.2、查看是否开启以及如何开启*

查看慢查询开启状态:SHOW VARIABLES LIKE ‘%slow_query_log%’;

开启慢查询:set global slow_query_log=1;

*2.2.2.3、慢查询工作原理*

这个是由参数long_query_time控制,默认情况下long_query_time的值为10秒,

命令:SHOW VARIABLES LIKE ‘long_query_time%’;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BukngjCZ-1614847542367)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps454.png)]

可以使用命令修改,如果使用命令就是暂时的,当mysql服务重启后,这个修改就不存了,会恢复到原来的默认值:set global long_query_time=5

也可以在my.cnf参数里面修改,这里的修改就是配置文件的修改,是永久性的:

【mysqld】下配置:

slow_query_log=1;

slow_query_log_file=/var/lib/mysql/atguigu-slow.log

long_query_time=3;

log_output=FILE

*假如运行时间正好等于long_query_time的情况,并不会被记录下来。也就是说,*

*在mysql源码里是判断大于long_query_time,而非大于等于。*

*2.2.2.4、查询当前系统*有多少慢查询记录*

show global status like ‘%Slow_queries%’;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VtXDyCyA-1614847542368)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps455.png)]

*2.3、批量插入数据脚本*

插入1qw条数据

*2.3.1、建库建表*

# 新建库

create database bigData;

use bigData;

#1 建表dept

CREATE TABLE dept(

id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT,

deptno MEDIUMINT UNSIGNED NOT NULL DEFAULT 0,

dname VARCHAR(20) NOT NULL DEFAULT “”,

loc VARCHAR(13) NOT NULL DEFAULT “”

) ENGINE=INNODB DEFAULT CHARSET=UTF8 ;

#2 建表emp

CREATE TABLE emp

(

id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT,

empno MEDIUMINT UNSIGNED NOT NULL DEFAULT 0, /编号/

ename VARCHAR(20) NOT NULL DEFAULT “”, /名字/

job VARCHAR(9) NOT NULL DEFAULT “”,/工作/

mgr MEDIUMINT UNSIGNED NOT NULL DEFAULT 0,/上级编号/

hiredate DATE NOT NULL,/入职时间/

sal DECIMAL(7,2) NOT NULL,/薪水/

comm DECIMAL(7,2) NOT NULL,/红利/

deptno MEDIUMINT UNSIGNED NOT NULL DEFAULT 0 /部门编号/

)ENGINE=INNODB DEFAULT CHARSET=UTF8;

*2.3.2、设置参数*

创建函数,假如报错:This function has none of DETERMINISTIC…

# 由于开启过慢查询日志,因为我们开启了 bin-log, 我们就必须为我们的function指定一个参数。

show variables like ‘log_bin_trust_function_creators’;

set global log_bin_trust_function_creators=1;

# 这样添加了参数以后,如果mysqld重启,上述参数又会消失,永久方法:

windows下my.ini[mysqld]加上log_bin_trust_function_creators=1

linux下 /etc/my.cnf下my.cnf[mysqld]加上log_bin_trust_function_creators=1

*2.3.3、创建函数*

*保证每条数据都不同*

1、随机产生字符串:

DELIMITER $$

CREATE FUNCTION rand_string(n INT) RETURNS VARCHAR(255)

BEGIN ##方法开始

DECLARE chars_str VARCHAR(100 ) DEFAULT

‘abcdefghijklmnopqrstuvwxyzABCDEFJHIJKLMNOPQRSTUVWXYZ’;

##声明一个 字符窜长度为 100 的变量 chars_str ,默认值是所有大小写字母

DECLARE return_str VARCHAR(255) DEFAULT ‘’;

DECLARE i INT DEFAULT 0;

##循环开始

WHILE i < n DO

SET return_str =CONCAT(return_str,SUBSTRING(chars_str,FLOOR(1+RAND()*52),1));

##concat 连接函数 ,substring(a,index,length) 从index处开始截取

SET i = i + 1;

END WHILE;

RETURN return_str;

END $$

#假如要删除

#drop function rand_string;

2、随机产生部门编号:

#用于随机产生部门编号

DELIMITER $$

CREATE FUNCTION rand_num( )

RETURNS INT(5)

BEGIN

DECLARE i INT DEFAULT 0;

SET i = FLOOR(100+RAND()*10);

RETURN i;

END $$

#假如要删除

#drop function rand_num;

*2.3.4、创建存储过程*

1、创建往emp表中插入数据的存储过程

DELIMITER $$

CREATE PROCEDURE insert_emp10000(IN START INT(10),IN max_num INT(10))

BEGIN

DECLARE i INT DEFAULT 0;

#set autocommit =0 把autocommit设置成0 ;提高执行效率

SET autocommit = 0;

REPEAT ##重复

SET i = i + 1;

INSERT INTO emp10000 (empno, ename ,job ,mgr ,hiredate ,sal ,comm ,deptno ) VALUES ((START+i) ,rand_string(6),‘SALESMAN’,0001,CURDATE(),FLOOR(1+RAND()*20000),FLOOR(1+RAND()*1000),rand_num());

UNTIL i = max_num ##直到 上面也是一个循环

END REPEAT; ##满足条件后结束循环

COMMIT; ##执行完成后一起提交

END $$

#删除

# DELIMITER ;

# drop PROCEDURE insert_emp;

2、往dept表中插入数据的存储过程

#执行存储过程,往dept表添加随机数据

DELIMITER $$

CREATE PROCEDURE insert_dept(IN START INT(10),IN max_num INT(10))

BEGIN

DECLARE i INT DEFAULT 0;

SET autocommit = 0;

REPEAT

SET i = i + 1;

INSERT INTO dept (deptno ,dname,loc ) VALUES (START +i ,rand_string(10),rand_string(8));

UNTIL i = max_num

END REPEAT;

COMMIT;

END $$

#删除

# DELIMITER ;

# drop PROCEDURE insert_dept;

*2.3.5、调用存储过程*

调用dept:

DELIMITER ;

CALL insert_dept(100,10);

#执行存储过程,往emp表添加50万条数据

DELIMITER ; #将 结束标志换回 ;

CALL insert_emp(100001,500000);

CALL insert_emp10000(100001,10000);

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rqSSOtxV-1614847542368)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps456.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Fgg7C4YK-1614847542369)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps457.png)]

*2.4、show profil*

*2.4.1、show profile是什么*

是mysql提供可以用来分析当前会话中语句执行的资源消耗情况。可以用于SQL的调优的测量

官网:http://dev.mysql.com/doc/refman/5.5/en/show-profile.htm

默认情况下,参数处于关闭状态,并保存最近15次的运行结果

*2.4.2、分析步骤*

1、查看当前的mysql是否支持show profile

Show variables like ‘profiling’;

默认是关闭,使用前需要开启

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-J5eoNX3m-1614847542370)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps458.jpg)]

2、开启功能,默认是关闭,使用前需要开启

show variables like ‘profiling’;

set profiling=1;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lplXN9YY-1614847542370)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps459.jpg)]

3、运行sql语句

select * from emp group by id%10 limit 150000;

select * from emp group by id%20 order by 5

这两条语句只是为了让它查询的时间变长,方便我们在后台查看,我们已经开启了show profile,开启之后我们所有发过的指令都会被记录下来。

4、查看结果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IC9gbujz-1614847542370)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps460.jpg)]

第一列是我们在开启show profile之后,一共执行过多少次命令,第二列是执行的时间,第三列是我们执行的sql语句。

5、诊断sql

show profile cpu,block io for query n ;(n为上一步前面的问题SQL数字号码);

先来查询一下query3:show profile cpu,block io for query 3;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FVpcz4zn-1614847542371)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps461.jpg)]

这是一句sql命令完整的生命周期。

诊断sql不止可以诊断cpu和io,还有其他的参数:

ALL --显示所有的开销信息

BLOCK IO --显示块IO相关开销

CONTEXT SWITCHES --上下文切换相关开销

CPU --显示CPU相关开销信息

IPC --显示发送和接收相关开销信息

MEMORY --显示内存相关开销信息

PAGE FAULTS --显示页面错误相关开销信息

SOURCE --显示和Source_function,Source_file,Source_line相关的开销信息

SWAPS --显示交换次数相关开销的信息

6、日常开发需要注意的结论

converting HEAP to MyISAM 查询结果太大,内存都不够用了往磁盘上搬了。

Creating tmp table 创建临时表

Copying to tmp table on disk 把内存中临时表复制到磁盘,危险!!!

locked

以上面的query8为例:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LYh8NpzV-1614847542371)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps462.jpg)]

出现了tmp table临时表,现在如果有1千万多万条数据,我们查询出来的符合要求的有900万条,然后我们要把这900万条数据复制到临时表里面,用完之后再删除,这是很耗性能的,所以这里的这四个都是不能出现的。

*2.5、全局查询日志*

*注意:这个只能在测试的时候用,绝对不能在生产环境中使用*

*2.5.1、配置文件开启*

在mysql的my.cnf中,设置如下:

#开启

general_log=1

# 记录日志文件的路径

general_log_file=/path/logfile

#输出格式

log_output=FILE

*2.5.2、sql命令开启*

set global general_log=1;

#全局日志可以存放到日志文件中,也可以存放到Mysql系统表中。存放到日志中性能更好一些,存储到表中

set global log_output=‘TABLE’;

此后 ,你所编写的sql语句,将会记录到mysql库里的general_log表,可以用下面的命令查看

select * from mysql.general_log;

*3、Mysql锁机制*

*3.1、概述*

*3.1.1、定义*

锁是计算机协调多个进程或线程并发访问某一资源的机制。

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

*3.1.2、生活中的例子*

打个比方,我们到淘宝上买一件商品,商品只有一件库存,这个时候如果还有另一个人买,

那么如何解决是你买到还是另一个人买到的问题?

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-473OQ2KT-1614847542371)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps463.png)]

这里肯定要用到事务,我们先从库存表中取出物品数量,然后插入订单,付款后插入付款表信息,然后更新商品数量。在这个过程中,使用锁可以对有限的资源进行保护,解决隔离和并发的矛盾。

*3.2、锁的分类*

*3.2.1、对数据的操作类型(读/写)分*

读锁(共享锁):针对同一份数据,多个读操作可以同时进行而不会互相影响。

写锁(排它锁):当前写操作没有完成前,它会阻断其他写锁和读锁。

*3.2.2、对数据操作的粒度分*

行锁

表锁

*3.3、三锁*

*3.3.1、表锁(偏读)*

*3.3.1.1、特点*

偏向MyISAM存储引擎,开销小,加锁快;无死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低

*3.3.1.2、案例分析(读锁)*
*3.3.1.2.1、建表与基本操作*

【表级锁分析–建表SQL】

create table mylock(

id int not null primary key auto_increment,

name varchar(20)

)engine myisam;

insert into mylock(name) values(‘a’);

insert into mylock(name) values(‘b’);

insert into mylock(name) values(‘c’);

insert into mylock(name) values(‘d’);

insert into mylock(name) values(‘e’);

select * from mylock;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-poJBi9a2-1614847542372)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps464.jpg)]

【查看表上加过的锁】

show open tables;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ECm1qep8-1614847542372)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps465.jpg)]

当前状态下没有锁,看In_use参数即可,这里是0就是没有锁。

【手动增加表锁】

lock table 表名字1 read(write),表名字2 read(write),表名字n read(write);

lock table mylock read,test03 write;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JVySUPyv-1614847542372)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps466.jpg)]

将mylock这个表设置一个read读锁,test03这个表设置一个write写锁

【释放表锁】

unlock tables;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-baZWDlQq-1614847542373)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps467.jpg)]

*3.3.1.2.2、开启两个端口操作这个表*

1、先将mylock这个表设置一个表锁,然后进行查看mylock表。开启另外一个端口,在另外一个端口查看这个表

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mvW4UFAf-1614847542373)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps468.jpg)] [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zhAL6jv5-1614847542375)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps469.jpg)]

因为读锁是共享锁,所以多个端口也是共同访问的。

2、查看能不能进行update的写操作

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PkFHgRin-1614847542375)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps470.jpg)]

虽然设置的是读锁,但是写操作一样不能进行,锁还没有释放,当前框能做的只有查看自己的表。

3、查看能不能查看test03表

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HOUGLNUy-1614847542375)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps471.jpg)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GGucnlS3-1614847542376)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps472.jpg)]

设了读锁的不能查看其它表,新的mysql对话可以访问别的表

4、新的mysql对话能否对设置读锁的表进行更改

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-B98bpNJw-1614847542378)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps473.jpg)]

输入更新的命令之后,一直在等待,没有任何反应,因为此时mylock表还在锁着,无法对其进行更新操作。专业名词叫阻塞。

将mylock解锁

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YM0qz6co-1614847542378)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps474.jpg)]

再看黑底的mysql对话

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-s4EayG2w-1614847542379)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps475.jpg)]

这里的update已经操作成功,说明,只有锁被释放了,其他对话框才能对加锁的表进行更新的操作。

*3.3.1.3、案例分析(写锁)*

1、mylock表设置写锁

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OKzDwuMQ-1614847542379)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps476.jpg)]

2、查看能否读自己加写锁的表

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ckYZ0Id0-1614847542380)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps477.jpg)]

可以读自己加写锁的表

3、能否改自己锁过的表

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AJszRRRB-1614847542381)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps478.jpg)]

可以

4、能否读其他表

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ucxK9Vqy-1614847542381)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps479.jpg)]

不可以

5、新建会话能否读加写锁的表

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NapYDqgF-1614847542382)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps480.jpg)]

阻塞,等待解锁

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sKYHaYjn-1614847542383)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps481.jpg)]

解锁之后,查看成功

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-69Klnren-1614847542383)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps482.jpg)]

*注意:有些时候,新会话查询在没有解锁的情况下也能查询成功,那是因为当我们查询同一个数据次数多了,mysql会自动将数据加入到缓存里,而多次访问之后,就会直接从缓存里面取数据,所以最好每次换不同的条件,例如(每次都换个不同的id)*

6、新建会话能否读其他没加锁的表

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0HizHaqK-1614847542383)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps483.jpg)]

可以

*3.3.1.4、结论*

MyISAM在执行查询语句(SELECT)前,会自动给涉及的所有表加读锁,在执行增删改操作前,会自动给涉及的表加写锁。

MySQL的表级锁有两种模式:

表共享读锁(Table Read Lock)

表独占写锁(Table Write Lock)

锁类型 他人可读 他人可写
读锁
写锁

结论:

结合上表,所以对MyISAM表进行操作,会有以下情况:

1、对MyISAM表的读操作(加读锁),不会阻塞其他进程对同一表的读请求,但会阻塞对同一表的写请求。只有当读锁释放后,才会执行其它进程的写操作。

2、对MyISAM表的写操作(加写锁),会阻塞其他进程对同一表的读和写操作,只有当写锁释放后,才会执行其它进程的读写操作。

*简而言之,就是读锁会阻塞写,但是不会堵塞读。而写锁则会把读和写都堵塞*

*3.3.1.5、表锁分析*

查看哪些表被加锁了

Show open tables;

如何分析表锁定

可以通过检查table_locks_waited和table_locks_immediate状态变量来分析系统上的表锁定:

Show status like‘table%’;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JNGLHh5y-1614847542384)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps484.jpg)]

table_locks_immediate:产生表级锁定的次数,表示可以立即获取锁的查询次数,每立即获取一次锁值+1

table_locks_waited:出现表级锁定争用而发生等待的次数(不能立即获取锁的次数,每等待一次锁值+1),此值高说明存在着较严重的表级锁争用情况

此外,myisam的读写锁调度是写优先,这也是myisam不适合做写为主表的引擎。因为写锁后,其他线程不能做任何操作,大量的更新会使查询很难得到锁,从而造成永久阻塞。

*3.3.2、行锁*

*3.3.2.1、特点*

偏向InnoDB存储引擎,开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。

InnoDB与MyISAM的最大不同有两点:一是支持事务(TRANSACTION);二是采用了行级锁

*3.3.2.2、行锁支持事务,复习老知识*

1、事务及其ACID属性:

事务是由一组SQL语句组成的逻辑处理单元,事务具有以下4个属性,通常简称为事务的ACID属性。

l 原子性(Atomicity):事务是一个原子操作单元,其对数据的修改,要么全都执行,要么全都不执行。

l 一致性(Consistent):在事务开始和完成时,数据都必须保持一致状态。这意味着所有相关的数据规则都必须应用于事务的修改,以保持数据的完整性;事务结束时,所有的内部数据结构(如B树索引或双向链表)也都必须是正确的。

l 隔离性(Isolation):数据库系统提供一定的隔离机制,保证事务在不受外部并发操作影响的“独立”环境执行。这意味着事务处理过程中的中间状态对外部是不可见的,反之亦然。

l 持久性(Durable):事务完成之后,它对于数据的修改是永久性的,即使出现系统故障也能够保持。

2、并发事务处理带来的问题

*更新丢失:*

当两个或多个事务选择同一行,然后基于最初选定的值更新该行时,由于每个事务都不知道其他事务的存在,就会发生丢失更新问题--最后的更新覆盖了由其他事务所做的更新。

例如,两个程序员修改同一java文件。每程序员独立地更改其副本,然后保存更改后的副本,这样就覆盖了原始文档。最后保存其更改副本的编辑人员覆盖前一个程序员所做的更改。

如果在一个程序员完成并提交事务之前,另一个程序员不能访问同一文件,则可避免此问题。

*脏读:*

一个事务正在对一条记录做修改,在这个事务完成并提交前,这条记录的数据就处于不一致状态;这时,另一个事务也来读取同一条记录,如果不加控制,第二个事务读取了这些“脏”数据,并据此做进一步的处理,就会产生未提交的数据依赖关系。这种现象被形象地叫做”脏读”。

一句话:事务A读取到了事务B已修改但尚未提交的的数据,还在这个数据基础上做了操作。此时,如果B事务回滚,A读取

的数据无效,不符合一致性要求。

*不可重复读:*

在一个事务内,多次读同一个数据。在这个事务还没有结束时,另一个事务也访问该同一数据。那么,在第一个事务的两次读数据之间。由于第二个事务的修改,那么第一个事务读到的数据可能不一样,这样就发生了在一个事务内两次读到的数据是不一样的,因此称为不可重复读,即原始读取不可重复。一句话:一个事务范围内两个相同的查询却返回了不同数据。

*幻读:*

一个事务按相同的查询条件重新读取以前检索过的数据,却发现其他事务插入了满足其查询条件的新数据,这种现象就称为“幻读”。

一句话:事务A 读取到了事务B提交的新增数据,不符合隔离性。

*3.3.2.3、事务隔离级别*

脏读”、“不可重复读”和“幻读”,其实都是数据库读一致性问题,必须由数据库提供一定的事务隔离机制来解决。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nQxBhiHW-1614847542384)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps485.png)]

数据库的事务隔离越严格,并发副作用越小,但付出的代价也就越大,因为事务隔离实质上就是使事务在一定程度上 “串行化”进行,这显然与“并发”是矛盾的。同时,不同的应用对读一致性和事务隔离程度的要求也是不同的,比如许多应用对“不可重复读”和“幻读”并不敏感,可能更关心数据并发访问的能力。

常看当前数据库的事务隔离级别:show variables like ‘tx_isolation’;

*3.3.2.4、案例分析*
*3.3.2.4.1、建表*

create table test_innodb_lock (a int(11),b varchar(16))engine=innodb;

insert into test_innodb_lock values(1,‘b2’);

insert into test_innodb_lock values(3,‘3’);

insert into test_innodb_lock values(4,‘4000’);

insert into test_innodb_lock values(5,‘5000’);

insert into test_innodb_lock values(6,‘6000’);

insert into test_innodb_lock values(7,‘7000’);

insert into test_innodb_lock values(8,‘8000’);

insert into test_innodb_lock values(9,‘9000’);

insert into test_innodb_lock values(1,‘b1’);

建立索引

create index test_innodb_a_ind on test_innodb_lock(a);

create index test_innodb_lock_b_ind on test_innodb_lock(b);

查看test_innodb_lock表

select * from test_innodb_lock;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BN3zEL7f-1614847542385)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps486.jpg)]

*3.3.2.4.2、演示*

1、Mysql5.0之后会自动进行提交,为了演示行锁,先把自动提交关了

Set autocommit = 0;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-d2vxp56q-1614847542386)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps487.jpg)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HtiK3oni-1614847542387)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps488.jpg)]

2、两个会话对同一个数据进行更新操作

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VSvt29bt-1614847542387)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps489.jpg)]

会话二也进行更新操作,会话二阻塞

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eYMeGARw-1614847542387)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps490.jpg)]

会话一提交

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9eCIp0XE-1614847542388)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps491.jpg)]

查看会话二,会话二更新成功。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KN9ARKUM-1614847542388)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps492.jpg)]

3、两个会话对不同的数据进行更新操作

会话一对b=b2进行操作

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Fdbyc2td-1614847542389)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps493.jpg)]

因为我们把自动提交关闭了,所以这里要对会话一进行操作,就要先把会话二的数据提交,所以时间才会是16秒

会话二对b=3进行操作

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3gcbOEAt-1614847542389)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps494.jpg)]

直接执行成功,可见,如果不是对同一个数据进行操作是不会造成阻塞的。

*注意:*

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-p3al5LYp-1614847542389)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps495.jpg)]

会话一和会话二都是设置了不自动提交,新开的会话三是默认的自动提交。

会话一更新了b的数据,然后进行提交,然后在会话二里面进行查看,发现会话二里面显示的数据,并不是更新后的数据,而会话三显示的数据就是更新后的数据。

会话二进行提交之后,再次查询,数据已经更新完毕

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZxWEetzo-1614847542389)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps496.jpg)]

*3.3.2.5、无索引或索引失效-行锁升级为表锁*
Session_1 Session_2
正常情况,各自锁定各自的行,互相不影响,一个2000另一个3000
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-903Wx6TD-1614847542389)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps497.png)] [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CxY2jrny-1614847542390)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps498.png)]
由于在column字段b上面建了索引,如果没有正常使用,会导致行锁变表锁
比如没加单引号导致索引失效,行锁变表锁(b是char类型)[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZzKj1Owg-1614847542391)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps499.png)] 被阻塞,等待。只到Session_1提交后才阻塞解除,完成更新[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qc0rvjMu-1614847542391)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps500.png)]

这个实验当中,我们修改的是不同的数据,如果是行锁,是可以实现更新操作的,不需要等待的时间。但是这里却出现了阻塞,等到会话一commit之后,会话二才操作成功。

这种现象就叫做行锁变表锁,有索引的时候,特别要注意,不能让索引失效了,索引失效不仅会让性能下降,也会让行锁变成表锁。

*3.3.2.6、select也可以加锁(锁定某一行)*

1、共享锁(Share Lock)

共享锁又称读锁,是读取操作创建的锁。其他用户可以并发读取数据,但任何事务都不能对数据进行修改(获取数据上的排他锁),直到已释放所有共享锁。

如果事务T对数据A加上共享锁后,则其他事务只能对A再加共享锁,不能加排他锁。获取共享锁的事务只能读数据,不能修改数据。

用法

SELECT … LOCK IN SHARE MODE;

在查询语句后面增加 LOCK IN SHARE MODE ,Mysql会对查询结果中的每行都加共享锁,当没有其他线程对查询结果集中的任何一行使用排他锁时,可以成功申请共享锁,否则会被阻塞。其他线程也可以读取使用了共享锁的表(行),而且这些线程读取的是同一个版本的数据。

2、排他锁(eXclusive Lock)

排他锁又称写锁,如果事务T对数据A加上排他锁后,则其他事务不能再对A加任任何类型的*。获准排他锁的事务既能读数据,又能修改数据。排他的意思就是,A给B加了锁,那A就独占B,其他的会话不能对其进行操作。

*用法:*

SELECT … FOR UPDATE;

给a=1加排他锁

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uMqy2oML-1614847542391)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps501.jpg)]

Session2想更改a=1的值,只能阻塞

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9ZTrc9DT-1614847542391)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps502.jpg)]

Session1进行commit提交

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-p8UleKxf-1614847542391)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps503.jpg)]

Session更新成功

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1VTArqDo-1614847542392)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps504.jpg)]

在查询语句后面增加 FOR UPDATE ,Mysql会对查询结果中的每行都加排他锁,当没有其他线程对查询结果集中的任何一行使用排他锁时,可以成功申请排他锁,否则会被阻塞。

*3.3.2.7、间隙锁的危害*

间隙锁带来的插入问题

Session_1 Session_2
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dvzxE0e4-1614847542392)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps505.png)] 阻塞产生,暂时不能插入[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LXn8xXTs-1614847542392)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps506.png)]
commit; 阻塞解除,完成插入[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8KCT8S5n-1614847542393)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps507.png)]

Session1中我们做的是更新操作,将a字段从1到5全部更新一遍,但是在这个表里面,a的值是没有2的,同时在session2中插入a=2的数据,按道理来说,这是行锁,对不同数据的操作应该是没问题的,但是session2却阻塞了,直到session1 commit后,才成功。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bRFMeVsH-1614847542393)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps508.jpg)]

Session1当中,a的值是2,3,4,实际上是没有2的,但是mysql为了保证数据的完整性,它会一起把2也给锁了,所以session2对a=2的数据进行操作,也是会造成阻塞的。

【什么是间隙锁】

当我们用****范围条件****而不是相等条件检索数据,并请求共享或排他锁时,InnoDB会给符合条件的已有数据记录的索引项加锁;对于键值在条件范围内但并不存在的记录,叫做“间隙(GAP)”,

InnoDB也会对这个“间隙”加锁,这种锁机制就是所谓的间隙锁(GAP Lock)。

【危害】

因为Query执行过程中通过过范围查找的话,他会锁定整个范围内所有的索引键值,即使这个键值并不存在。

间隙锁有一个比较致命的弱点,就是当锁定一个范围键值之后,即使某些不存在的键值也会被无辜的锁定,而造成在锁定的时候无法插入锁定键值范围内的任何数据。在某些场景下这可能会对性能造成很大的危害

*3.3.2.8、案例结论*

****Innodb****存储引擎由于实现了行级锁定,虽然在锁定机制的实现方面所带来的性能损耗可能比表级锁定会要更高一些,但是在整体并发处理能力方面要远远优于MyISAM的表级锁定的。当系统并发量较高的时候,Innodb的整体性能和MyISAM相比就会有比较明显的优势了。

但是,Innodb的行级锁定同样也有其脆弱的一面,当我们使用不当的时候,可能会让Innodb的整体性能表现不仅不能比MyISAM高,甚至可能会更差。

*3.3.2.9、行锁分析*

【如何分析行锁定】

通过检查InnoDB_row_lock状态变量来分析系统上的行锁的争夺情况

show status like ‘innodb_row_lock%’;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xM2wZPBr-1614847542394)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps509.jpg)]

*这里是因为我挂机吃饭去了,所以数据有点异常,数据太大了*

对各个状态量的说明如下:

Innodb_row_lock_current_waits:当前正在等待锁定的数量;

Innodb_row_lock_time:从系统启动到现在锁定总时间长度;

Innodb_row_lock_time_avg:每次等待所花平均时间;

Innodb_row_lock_time_max:从系统启动到现在等待最常的一次所花的时间;

Innodb_row_lock_waits:系统启动后到现在总共等待的次数;

对于这5个状态变量,比较重要的主要是

Innodb_row_lock_time_avg(等待平均时长),

Innodb_row_lock_waits(等待总次数)

Innodb_row_lock_time(等待总时长)这三项。

尤其是当等待次数很高,而且每次等待时长也不小的时候,我们就需要分析系统中为什么会有如此多的等待,然后根据分析结果着手指定优化计划。

最后可以通过

SELECT * FROM information_schema.INNODB_TRX\G;

来查询正在被锁阻塞的sql语句。

*3.3.2.10、行锁优化建议*

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KPCq42NT-1614847542394)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps510.jpg)]

*3.3.3、页锁*

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wznDQeMf-1614847542394)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps511.jpg)]

*4、主从复制*

*4.1、复制的基本原理*

Slave会从master读取binlog来进行数据同步

原理图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yKDt3uaW-1614847542394)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps512.png)]

三步骤:

1、master将改变记录到二进制日志(binary log)。这些记录过程叫做二进制日志事件,binary log events

2、slave将master的binary log events拷贝到它的中继日志(relay log)

3、slave重做中继日志中的事件,将改变应用到自己的数据库中。

Mysql的复制是异步的且串行化的。

*4.2、复制的基本原则*

1.每个slave只有一个master

2、每个slave只能有一个唯一的服务器ID

3、每个master可以有多个slave

*4.3、复制的最大问题*

网络延时

*4.4、主从常见配置*

*4.4.1、主从机的配置文件配置*

1、mysql版本一致,且后台以服务运行,不管主机与从机都是同一个系统,还是一个是windows,一个是linux,因为数据库之间要实现主从,那他们就必须能够通信,先用命令查询服务器的ip地址,然后互相去ping对方,能ping通才能继续下一步。

2、主从配置都在mysqld节点下,都是小写,配置文件是my.ini或者my.cnf

*主机修改配置文件:*

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kuoXZA5d-1614847542395)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps513.png)]

(1)、[必须]主服务器ID:server-id=1

(2)、[必须]启用二进制日志:log-bin=自己本地的路径/data/mysqlbin

例如:log-bin=D:/devSoft/MySQLServer5.5/data/mysqlbin

(3)、[可选]启用错误日志:log-err=自己本地的路径/data/mysqlerr

例如:log-err=D:/devSoft/MySQLServer5.5/data/mysqlerr

(4)、[可选]根目录:basedir=“自己本地路径”

例如:basedir=“D:/devSoft/MySQLServer5.5/”

(5)、[可选]临时目录:tmpdir=“自己本地路径”

例如:tmpdir=“D:/devSoft/MySQLServer5.5/”

(6)、[可选]数据目录:datadir=“自己本地路径/Data/”

例如:datadir=“D:/devSoft/MySQLServer5.5/Data/”

(7)、read-only=0:主机,读写都可以

(8)、[可选]设置不要复制的数据库:binlog-ignore-db=mysql

(9)、[可选]设置需要复制的数据库:binlog-do-db=需要复制的主数据库名字

从机修改配置文件:我的从机是linux的

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gUVzREnD-1614847542395)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps514.png)]

[必须]从服务器唯一ID

[可选]启用二进制日志

因修改过配置文件,请主机+从机都重启后台mysql服务

*4.4.2、主从机都关闭防火墙*

windows手动关闭

关闭虚拟机linux防火墙 service iptables stop

Linux的如果不行,就去查看linux的笔记,查看防火墙部分

Linux端必须关闭防火墙,不然本机去ping虚拟机是ping不通的,本机和虚拟机要在同一个网段上,设置虚拟机的时候要注意分配id地址要跟本机的网段一样,具体可以参考博客。

Window也可以直接关闭防火墙,这样虚拟机就可以ping通本机了,但是本机也可以不关闭防火墙,下面是window不关闭防火墙,也能让虚拟机ping通的做法。

打开本地的防火墙,找到高级设置

在windows defender 防火墙属性那里,将入站连接设置为允许

点击入站规则,找到文件和打印机共享(IPV4-in),一定要是配置文件那一列是专用和公用那一列,然后将其设置为是。

即可ping通。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AqTlvUvW-1614847542396)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps515.jpg)] [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kglLwsM9-1614847542396)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps516.jpg)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RfbiEnid-1614847542397)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps517.jpg)]

*4.4.3、在windows主机上建立账户并授权给slave*

1、GRANT REPLICATION SLAVE ON . TO ‘随意的名字’@‘从机器数据库IP’ IDENTIFIED BY ‘密码’;

例如:GRANT REPLICATION SLAVE ON . TO ‘zhangsan’@‘192.168.14.167’ IDENTIFIED BY ‘123456’;

赋予权限之后,还需要刷新一下权限,权限才能生效。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-40sjKj10-1614847542397)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps518.jpg)]

这里是创建一个账户,所以名字可以自己随便取,只要后面的ip是从机的ip即可,密码也是自己随意设置,,但是后面在从机里设置名字和密码的时候,要和这里一致。

2、查询master的状态:show master status;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FNjqAwt4-1614847542398)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps519.png)]

记录下file和position的值,意思就是,从机要从mysqlbin.000005这个文件的第1267行开始复制。

如果有很多二进制文件,那master需要的二进制文件一定是最后一个。

3、注意:执行完此步骤后不要再操作主服务器MYSQL,防止主服务器状态值变化

*4.4.4、在linux从机上配置需要复制的主机*

1、

CHANGE MASTER TO MASTER_HOST=‘主机ip’,

MASTER_USER=‘zhangsan’,

MASTER_PASSWORD=‘123456’,

MASTER_LOG_FILE=‘上面file的名字’,MASTER_LOG_POS=上面position的值;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZEzbsWJV-1614847542399)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps520.png)]

2、启动slave复制功能

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cVVghOwi-1614847542399)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps521.jpg)]

3、查看slave的状态

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PqpmxPQ6-1614847542400)(file:///C:\Users\FXP\AppData\Local\Temp\ksohtml8860\wps522.png)]

这两个数据都是yes说明配置成功

因为很多原因,可能是环境的问题,会导致这两个不是同时为yes,可以看第一点里的图片,可能在之前就已经启动过从机或者其他原因,那就要先把从机关闭,stop slave,然后重新再进行一遍流程,但是要注意,这里重新开始的流程,position已经不是原来的position,因为我们已经在数据库里做了一些操作,每次重新启动slave之前,都要查询一下,show master status,查看position在哪个位置。

*4.4.5、开始主从复制*

全部配置成功之后,我们就可以在主机建库建表,然后对数据进行一些操作,这些操作都会在从机中记录下来,例如我们在主机建了一张表,建好之后,我们可以在从机中查到表里的数据,这就是主从复制。

上一篇:Java中设计模式之单例设计模式-1


下一篇:spring boot:使mybatis访问多个druid数据源(spring boot 2.3.2)