2021-07-16 mysql笔记

一、常用命令
1.mysql服务开启,net start 服务名
2.mysql服务关闭,net stop 服务名
3.登陆mysql,mysql -u用户名 -p
4.登出mysql,exit
5. 显示所有的数据库,show databases;
6. 使用某一个库,use 库名;
7. 显示当前库下的所有的表,show tables;
8. 创建一个库,create database 库名;
9. 删除一个库,drop database 库名;
10.查看某个表的结构,desc 表名;
10. 显示某个库下所有的表,show tables from 库名;
11. 显示当前使用的数据库,select database();
12. 查看数据版本,select version();
13. 查数据,select * from 表名;
14. 插入数据,insert into 表名(字段1,字段2…) values(值1,值2…)
15. 更新数据,update 表名 set 字段 = 值 where + 条件
16. 删除数据,delete from 表名 where + 条件

二、基操
1.查询常量
select 100;

2.查询表达式
select 100%98 AS result;

3.查询表中的字段
SELECT
id,
first_name,
last_name,
email,
phone,
job_id,
salary,
commission_pet,
manager_id,
department_id,
hiredate
FROM
employee;

4.起别名
1)通过as
select first_name as 姓,last_name as 名 from employee;

2)通过空格
select first_name 姓,last_name 名 from employee;

5.去重
select distinct department_id from employee;

6.concat,连接内容
select concat(first_name,last_name) as 姓名 from employee;

select concat(first_name,’,’,last_name,’,’,email,’,’,ifnull(commission_pet,0)) As result from employee;

7.条件运算符,> ,<,=,!=,<=>(安全等于,可以判断某个字段是否等于null,也可以判断普通数值),<>(不等于),>=,<=
1)查询工资大于1200的员工信息
select * from employee where salary > 1200;

2)查询部门id不等于1的员工名称和部门id
select last_name,department_id from employee where department_id != 1;

select last_name,department_id from employee where department_id <> 1;

8.逻辑运算符(用于连接条件表达式),&&,|| ,! ,and,or,not
1)查询工资大于1200小于10000的员工信息
select * from employee where salary > 1200 and salary < 10000;

2)查询部门id不在10到20之间,或者工资高于10000的员工信息
select * from employee where department_id <= 10 or department_id > 20 or salary >10000;
或者
select * from employee where Not(department_id >= 10 and department_id <= 20) or salary > 10000;

9.模糊查询,like,between and,in,is null,is not null
1)like,一般集合通配符使用,%代表任意个字符,包含0个字符,_代表单个字符
1.1查询名包含兰的员工信息
select * from employee where last_name like’%兰%’;

1.2查询名第三个字符为a,第五个字符为b的员工信息
select * from employee where last_name like’__a_b%’;

1.3查询名第二个字符为_的员工信息
在这里插入图片描述

或者
在这里插入图片描述

2)between and
查询部门id在1到10之间的员工信息
select * from employee where department_id >= 1 and department_id <= 10;
或者

select * from employee where department_id between 1 and 10;

3)in(判断某个字段是否为列表中的某一项)
查询工种编号为IT_001,IT_002其中一个的员工信息
select * from employee where job_id in(‘IT_001’,‘IT_002’);

4)is null(注意:判断某个字段只能使用is null 或者is not null,不能使用=null,!=null)
查询没有奖金的员工信息
select * from employee where commission_pet is null;

10.练习题
1)查询没有奖金,且工资大于8000的员工信息
select * from employee where commission_pet is null and salary > 800;

2)查询job_id不为’IT’,或者工资为10000的员工信息
select * from employee where job_id <> ‘IT’ or salary = 8000;
或者
select * from employee where job_id != ‘IT’ or salary = 8000;

3)select * from employee

select * from employee where commission_pet like(’%%’) and last_name like(’%%’);
的查询结果一样吗?
不一样,%代表任意字符,但是commission_pet或者last_name有可能是空的,所以这两条sql语句查询结果是不一样的。

11.排序,order by 字段名 desc(降序)/asc(升序),默认查询数据是升序的
1)查询员工信息,工资从高到低排列
select * from employee order by salary desc;

2)查询部门id大于等于2的,且按照入职时间降序排序的员工信息
select * from employee where department_id >= 2 order by hiredate desc;

3)查询员工的信息,年薪,并且按照年薪排序(按表达式排序)
select * ,salary12(1+ifnull(commission_pet,0)) as 年薪
from employee
order by salary12(1+ifnull(commission_pet,0)) desc
4)查询员工的信息,年薪,并且按照年薪排序(按别名排序)
select * ,salary12(1+ifnull(commission_pet,0)) as 年薪
from employee
order by 年薪 desc

5)根据名字长度来排序
select * from employee order by length(last_name) desc

6)查询员工信息,先按照工资升序,再按照部门id降序(多字段排序)
select * from employee order by salary asc,department_id desc;

7)查询工资不在5000到8000的员工信息,并且按照工资降序
select * from employee where salary not between 5000 and 8000 order by salary desc;

8)查询邮箱中包含y的员工信息,并先按邮箱的字节数降序,再按部门id升序
select * from employee where email like ‘%e%’ order by length(email) desc,department_id asc;

12.函数
1)字符函数
1.1 length(),获取参数值的字节数,一个中文为3个字节
select length(“秀abc”);
在这里插入图片描述
1.2 concat(),拼接字符串
select concat(first_name,last_name) as 姓名 from employee;

1.3 upper()字母转大写,lower()字母转小写
select upper(“tom”);

select lower(“JERRY”);

1.4 substr(),substring()截取字符串
1.4.1
注意:索引从1开始,从索引5开始截取到后面的内容,包含索引5位置的字符
select substr(“likesingjumprap”,5)
结果
在这里插入图片描述

1.4.2
从索引1位置开始,截取4个字符
select substr(“likesingjumprap”,1,4)
在这里插入图片描述

1.4.3 . 将员工名中的首字母大写,其他字母小写。
select concat(upper(substr(last_name,1,1)),lower((substr(last_name,2)))) as result from employee;

1.5.instr(),返回子字符串第一次出现的索引,如果找不到返回0
select instr(‘hellovuejavavue’,‘vue’) as result;
在这里插入图片描述

1.6.trim(),去除字符串前后两端的空格
select trim(" h el lo ");
在这里插入图片描述

根据指定内容去除
select trim(‘aa’ from ‘aaa和立即bbbaaa’) as result;

在这里插入图片描述

1.7.lpad(),用指定长度的字符实现左填充内容
select LPAD(“hello”,5,"*") as result;

1.8.rpad(),用指定长度的字符实现右填充内容
select RPAD(“hello”,5,"*") as result;

1.9 replace() 替换
select replace(‘hellojava’,“java”,“vue”) as result;
在这里插入图片描述

2)数学函数
2.1 round(),四舍五入
select round(2.7);
结果为3

2.1.1
select round(-2.55);
结果-3

2.1.2 四舍五入并且保留指定的个数
select round(2.7789,1);
结果2.8

2.2 ceil(),向上取整,返回大于等于该参数的最小整数
select ceil(1.0001);
结果2

select ceil(-1.01);
结果-1

2.3 floor(),向下取整,返回小等于该参数的最小整数
select floor(-7.001);
结果-8

2.4 truncate(),截断,不包含0
select truncate(1.111,2);
结果1.11

2.5 mod(),取余
select mod(10,3);
结果1

3)日期函数

3.1 now(),获取当前系统日期+时间
select now();

3.2 curdate(),获取当前系统日期
select curdate();

3.3 curtime(),获取当前时间
select curtime();

3.4 可以获取指定的年份,月份,时,分秒
select year(now());
在这里插入图片描述

select month(now());
在这里插入图片描述

select monthname(now());
在这里插入图片描述

select day(now());
在这里插入图片描述

select hour(now());
在这里插入图片描述

select minute(now());
在这里插入图片描述

select second(now());
在这里插入图片描述

3.5 str_to_date 将字符通过指定格式转换成日期
注意:mysql的格式符
在这里插入图片描述select str_to_date(“1990-01-09”,"%Y-%m-%d") as result;

在这里插入图片描述

3.6 date_format(),将日期转换成字符串
select date_format(now(),"%Y年%c月%d日") as result

在这里插入图片描述

3.7 datediff(),比较两个日期相差多少天
select datediff(max(hiredate),min(hiredate)) from employee;
在这里插入图片描述

例子:获取员工名,入职日期,且其工资不为空
select
last_name,date_format(hiredate,"%Y年%c月%d日") as 入职日期
from employee
where salary is not null;

4)流程控制函数
4.1 if(),类似于java得if和else
select if(10 > 5,“a”,“b”) as result;
在这里插入图片描述
例子
SELECT
last_name,
commission_pet,
IF
( commission_pet IS NULL, “没有奖金”, “有奖金” ) AS remark
FROM
employee;

在这里插入图片描述

4.2 case(),类似于java的switch case
情景一:
case 要判断的字段或表达式
when 常量1 then 要显示的值1或者语句1;
when 常量2 then 要显示的值2或者语句2;
when 常量3 then 要显示的值3或者语句3;

else 要显示的值n或者语句n;
end

例子
SELECT
salary AS 原工资,
department_id,
CASE
department_id
WHEN 1 THEN
salary * 10
WHEN 2 THEN
salary * 10
WHEN 3 THEN
salary * 10 ELSE salary
END AS 新工资
FROM
employee;

在这里插入图片描述

情景二:

case
when 条件表达式1 then 要显示的值1或者语句1;
when 条件表达式2 then 要显示的值2或者语句2;
when 条件表达式3 then 要显示的值3或者语句3;

else 要显示的值n或者语句n;
end
例子

SELECT
salary AS 工资,
department_id AS 部门 id,
CASE
WHEN salary > 10000 THEN
‘A’
WHEN salary > 8000 THEN
‘B’
WHEN salary > 5000 THEN
‘C’ ELSE ‘D’
END AS 工资级别
FROM
employee;

在这里插入图片描述

4.3 练习
4.3.1 显示当前系统的日期+时间
select now();

4.3.2 查询员工姓名,工资,且工资提升20%作为新工资
SELECT
concat( first_name, last_name ) AS 姓名,
salary AS 工资,
( salary + salary * 0.2 ) AS 新工资
FROM
employee;

4.3.2 将员工的首字母姓名排序,并且写出姓名的长度
SELECT
*,
length( concat( first_name, last_name ) )
FROM
employee
ORDER BY
substr( concat( first_name, last_name ), 1 ) DESC;

5)分组函数,用作统计使用,又称为聚合函数或者统计函数或组函数
5.1 基本使用,一下几个常用的分组函数都是忽略null值的。
5.1.1 求最大值
select max(salary) from employee;

5.1.2 求最小值
select min(salary) from employee;

5.1.3 求平均值
select avg(salary) from employee;

5.1.4 求和
select sum(salary) from employee;

5.1.5 统计个数
select count(salary) from employee;
效率:
MYISAM存储引擎下,count(*)的效率高
INNODN存储引擎下,count(*)和count(1)的效率差不多,比count(字段)的效率要高

5.1.6 和distinct集合使用
select sum(distinct salary) from employee;

5.1.7 查询部门id为10的员工个数
select count(*) from employee where department_id = 10;

5.2 分组查询
5.2.1 语法:
select 分组函数,列(要求出现在group by后面)
from 表
where 条件
group by 列
order by 子句

5.2.2 查询每个工种的最大工资
select max(salary),job_id from employee group by job_id;
在这里插入图片描述

5.2.3 查询邮箱包含e字符的,每个部门的平均工资
select avg(salary),department_id from employee where email like ‘%e%’ group by department_id;
在这里插入图片描述

5.2.4 查询有奖金的每个领导手下员工的最高工资(分组前做筛选)
select max(salary),manager_id from employee where commission_pet is not null group by manager_id;

在这里插入图片描述

5.2.5 查询哪一个部门的员工个数>2(分组后做筛选)
select count(*),department_id from employee group by department_id having count(*) > 2;

5.2.6 查询每个工种有奖金的员工的最高工资>12000的工种编号和最高工资
select max(salary),job_id from employee where commission_pet is not null group by job_id having max(salary) > 12000;

5.2.7 查询领导id>100的每个领导手下的最低工资>5000的领导id是哪个,以及其最低工资。
select min(salary),manager_id from employee where manager_id > 100
group by manager_id having min(salary) > 5000;

5.2.8 按员工的名长度分组,查询每一组的员工个数,并且员工个数>5
select count(*),length(last_name) from employee group by length(last_name) havaing count(*) > 5;

5.2.9 查询每个部门每个工种的的员工的平均工资(多字段分组)
select avg(salary),department_id,job_id from employee group by department_id,job_id;

5.2.10 查询每个部门每个工种的的员工的平均工资且平均工资>10000,并且按照工资高低排序
select avg(salary),department_id,job_id from employee group by department_id,job_id having avg(salary) > 10000 order by avg(salary) desc;

5.2.11 查询各个管理者手下员工的最低工资,其中最低工资不低于7000,没有管理者的员工不计算在内
select min(salary),manager_id from employee where manager_id is not null group by manager_id having min(salary) >= 7000;

5.2.12 查出所有部门的编号,员工数量和工资平均值,并且按照平均工资排序
select department_id,count(*),avg(salary) from employee group by department_id order by avg(salary) desc;
在这里插入图片描述

6)连接查询,又称多表查询
6.1 笛卡尔积,假设表1有m行数据,表2有n行数据,那么联表查询就有m*n条数据(没有加过滤条件的情况)

6.2 连接查询分类

6.2.1 内连接(inner,inner可以省略)
1)等值连接
1.1) 查询员工名和对应的部门名
select last_name,department_name from employee,department where employee.department_id = department.department_id
1.2 )查询有奖金的员工名,部门名
select last_name,department_name from employee,department where employee.department_id = department.department_id and employee.commission_pet is not null;
1.3) 查询每个城市的部门个数
select count(*),city from department,location where department.location_id = location.location_id group by city;
1.4)查询有奖金的每个部门的部门名和部门领导的id和该部门的最低工资
select d.department_name,e.manager_id,min(salary) from employee e,department d where e.department_id = d.department_id and e.commission_pet is not null group by d.department_name,e.manager_id;
1.5)查询每个工种的工种名和员工的个数,并且按照员工个数排序
select count(*),j.job_title from employee e,jobs j where e.job_id = j.job_id group by j.job_title order by count(*) desc;
1.6)查询员工名,部门名,所在城市(3表)
select e.last_name,d.department_name,l.city from employee e,department d,location l where e.department_id = d.department_id and d.location_id = l.location_id
1.7)查询员工名,部门名
select e.last_name,d.department_name d from employee e inner join department d on e.department_id = d.department_id;
1.8)查询部门个数大于3的城市名和部门个数
select count(*),l.city from department d inner join location l on d.location_id = l.location_id group by l.city having count() > 3;
1.9)查询哪个部门的员工个数>1的部门名和员工个数,并且按照员工个数排序
select d.department_name,count(*) from department d inner join employee e on d.department_id = e.department_id group by d.department_name having count(
) > 1 order by count(*) desc;
2.0)查询员工名,部门名,工种名,并且按照部门名排序
select e.last_name,d.department_name,j.job_title from employee e inner join department d on e.department_id = d.department_id inner join jobs j on e.job_id = j.job_id order by d.department_name;
2)非等值连接
2.1)查询员工的薪资,以及工资级别为A
select e.salary,jg.level from employee e,job_grades jg where e.salary between jg.lower_sal and jg.higher_sal and jg.level = ‘A’;
2.2)查询员工的薪资,以及工资级别
select e.salary,jg.level from employee e inner join job_grades jg on e.salary between jg.lower_sal and jg.higher_sal;
2.3)查询工资级别的个数>3的个数,并且按照个数排序
select count(*),jg.level from employee e inner join job_grades jg on e.salary between jg.lower_sal and jg.higher_sal group by jg.level having count(*) > 3 order by count(*);
3)自连接
3.1)查询员工id,员工名,上级id,上级名,数据都取自一个表(employee)
select e.id,e.last_name,m.id,m.last_name from employee e,employee m where e.manager_id = m.id;
3.2) 查询名包含字符e的员工id,员工名,三级id,上级名
select e.id,e.last_name,m.id,m.last_name from employee e inner join employee m on e.manager_id = m.id where e.last_name like ‘%e%’;
6.2.2 外连接,以下outer可以省略
外连接特点:
外连接的查询结果为主表中的所有记录
如果从表中有和它匹配的,则显示匹配的值,
如果从表中没有和它匹配的,则显示null值
外连接结果 = 内连接结果 + 主表中有而从表没有的记录
1)左外连接 (left outer),left左边的是主表
1.1 查询没有男友的女生信息
select g.name, b.*from girl g left outer join boy b on g.boy_id = b.id where b.id is null;
1.2 查询哪一个部门没有员工
select d.*,e.id from department d left join employee e on d.department_id = e.department_id where e.id is null;
1.3查询部门名为开发部或者财务部的员工信息
select d.* ,e.* from department d left join employee e on d.department_id = e.department_id where d.department_name in (‘财务部’,‘开发部’) and e.id is not null;
2)右外连接 (right outer),right右边的是主表
2.1查询没有男友的女生信息
select g.name, b.*from boy b right outer join girl g on g.boy_id = b.id where b.id is null;
2.2 查询哪一个部门没有员工
select d.*,e.id from employee e right join department d on d.department_id = e.department_id where e.id is null
3)全外连接 (full outer)
全外连接 = 内连接的结果 + 表1中有但表2中没有 + 表2中有但表1中没有

6.2.3 交叉连接 cross(笛卡尔积)
select g.*,b.* from girl g cross join boy b;

7)子查询
select 后面:仅仅支持标量子查询
from 后面:支持表子查询
where和having后面:支持标量子查询,列子查询,行子查询
exists后面(相关子查询)
表子查询

标量子查询:结果集只有一行一列
列子查询:结果集只有一列多行
行子查询:结果集只有一行多列
表子查询:结果集一般为多行多列

7.1 标量子查询

7.1.1 查询工资比小明高的员工信息
select * from employee where salary > (select salary from employee where last_name = ‘小明’)
7.1.2 查询job_id与143员工一样,工资大于145号员工的员工的名,job_id和工资
select last_name,job_id,salary from employee where job_id = (select job_id from employee where id = 143) and salary > (select salary from employee where id = 145);
7.1.3 查询工资最低的员工的名,job_id,工资
select last_name,job_id,salary from employee where salary = (select min(salary) from employee);
7.1.4 查询最低工资大于10号部门最低工资的部门id,以及其最低工资
select department_id,min(salary) from employee group by department_id having min(salary) > (select min(salary) from employee where department_id = 10);

7.2 列子查询
操作符 含义
in/not in 等于列表中的任意一个
any/some 和子查询返回的某一个值比较
all 和子查询返回的所有值比较

7.2.1 返回location_id是1或者2的部门中的所有员工
select * from employee where department_id in(select distinct department_id from department where location_id in(1,2));
7.2.2 返回其他工种中比job_id为"IT_001"工种的任一工资低的员工的id,名,工种,工资
select id,last_name,job_id,salary from employee where salary < any(select distinct salary from employee where job_id = ‘IT_001’) and job_id <> ‘IT_001’;
7.2.3 返回其他工种中比job_id为"IT_001"工种的所有工资都低的员工的id,名,工种,工资
select id,last_name,job_id,salary from employee where salary < all(select distinct salary from employee where job_id = ‘IT_001’) and job_id <> ‘IT_001’;
或者
select id,last_name,job_id,salary from employee where salary < (select min(salary) from employee where job_id = ‘IT_001’) and job_id <> ‘IT_001’;

7.3 行子查询
7.3.1 查询员工id最小工资最高的员工信息
select * from employee where id = (select min(id) from employee) and salary=(select max(salary) from employee);
或者
select * from employee where(id,salary) = (select min(id),max(salary) from employee);

7.4 select后的子查询,仅支持标量子查询(即返回一行一列)
7.4.1 查询每个部门的员工个数
select d., (select count() from employee e where e.department_id = d.department_id) 人数
from department d;
7.4.2 查询员工id为2的部门名
select (select department_name from department d inner join employee e on d.department_id = e.department_id where e.id = 2) 部门;

7.5 from后面的子查询,将子查询的结果作为一张表,必须起别名
7.5.1 查询每个部门的平均工资工资等级
select A.*,B.level from (select avg(salary) ag,department_id from employee group by department_id) as A inner join job_grades B on A.ag between B.lower_sal and B.higher_sal;

7.6 exists后面(相关子查询)
语法:
exists(完整的查询语句)
结果:
返回1或者,1代表存在,0代表不存在
7.6.1 查询有员工的部门名
select department_name from department d where exists(select * from employee e where e.department_id = d.department_id);
或者
select department_name from department d where department_id in(select department_id from employee);
7.6.2 查询没girl的男人信息
select bo.* from boy bo where bo.id not in(select g.boy_id from girl g);
或者

select bo.* from boy bo where not exists(select g.boy_id from girl g where bo.id = g.boy_id);

7.7 子查询练习
7.7.1 查询和tom相同部门的员工的名和工资
select last_name,salary from employee where department_id = (select department_id from employee where last_name = ‘tom’);
7.7.2 查询工资比公司平均工资高的员工id,名和工资
select id,last_name,salary from employee where salary > (select avg(salary) from employee)
7.7.3 查询各部门中工资比本部门平均工资高的员工的id,名和工资
select A.id,A.last_name,A.salary from employee A inner join(select avg(salary) ag,department_id from employee group by department_id) as B where A.department_id = B.department_id and A.salary > B.ag;
7.7.4 查询和名中包含e的员工员工相同部门的员工id和名
select id,last_name from employee where department_id in (select distinct department_id from employee where last_name like ‘%e%’);
7.7.5 查询在部门的location_id为1的部门工作的员工的id
select e.id from employee e where e.department_id = any (select distinct d.department_id from department d where d.location_id = 1);
7.7.6 查询管理者为jerry的员工名和工资
select e1.id,e1.last_name,e1.salary from employee e1 inner join employee e2 on e1.id = e2.manager_id where e1.last_name = ‘jerry’;
或者
select last_name,salary from employee where manager_id in(select id from employee where last_name = ‘jerry’);
7.7.7 查询工资最高的员工的姓名,要求姓和名显示为一列
select concat(first_name,last_name) as 姓名,salary from employee where salary = (select max(salary) from employee);

7.7.8 查询选修了体育的所有学生学号
SELECT
s.sno
FROM
student s
INNER JOIN student_grade sg ON s.sno = sg.sno
INNER JOIN clazz c ON c.cno = sg.cno
WHERE
c.cname = ‘体育’;

7.7.9 查询即选修了英语,又选修了体育的学生信息
SELECT DISTINCT
s.sno,
s.NAME
FROM
student s
INNER JOIN student_grade sg ON sg.sno = s.sno
INNER JOIN clazz c ON sg.cno = c.cno
WHERE
s.sno IN (
SELECT
sg1.sno
FROM
student_grade sg1
INNER JOIN clazz c1 ON sg1.cno = c1.cno
INNER JOIN ( SELECT sno FROM student_grade sg2 INNER JOIN clazz c2 ON sg2.cno = c2.cno WHERE c2.cname = ‘体育’ ) AS A
WHERE
c1.cname = ‘英语’
AND sg1.sno = A.sno
)

7.7.10 查询有两门以上学科不及格的学生信息以及其平均成绩
SELECT
sno,
NAME,
ag
FROM
(
SELECT
sg.sno,
NAME,
avg( grade ) ag,
sum( CASE WHEN grade < 60 THEN 1 ELSE 0 END ) num
FROM
student_grade sg
INNER JOIN student s ON s.sno = sg.sno
GROUP BY
sg.sno,
NAME
HAVING
num >= 2
) a;

7.7.11 查询课程编号为1的成绩大于课程编号为2的所有学生学号
SELECT DISTINCT
sno
FROM
student_grade sg1
WHERE
(
SELECT
grade
FROM
student_grade sg2
INNER JOIN clazz c1 ON c1.cno = sg2.cno
WHERE
sg2.sno = sg1.sno
AND c1.cno = 1
) > (
SELECT
grade
FROM
student_grade sg3
INNER JOIN clazz c2 ON c2.cno = sg3.cno
WHERE
sg3.sno = sg1.sno
AND c2.cno = 2
);

8)分页查询
格式:
select … from . where . group by. having.order by. limit offset,size;
offset要显示每条条目的起始索引(起始索引从0开始)
size要显示的条目个数
8.1 查询前五条员工信息
select* from employee limit 0,5;
或者
select * from employee limit 5;

8.2 查询第11条到第20条数据
select * from employee limit 10,10;

8.3 公式
select … from 表 limit (page-1) * size,size

9)子查询练习
9.1 查询工资最低的员工信息
select * from employee where salary = (select min(salary) from employee);

9.2 查询平均工资最低的部门信息
SELECT
d.*
FROM
department d
WHERE
d.id = (
SELECT
department_id
FROM
employee
GROUP BY
department_id
HAVING
avg( salary ) = ( SELECT min( ag ) FROM ( SELECT avg( salary ) ag, department_id FROM employee GROUP BY department_id ) ag_edp )
)

或者
SELECT
d.*
FROM
department d
WHERE
d.id = ( SELECT department_id FROM employee GROUP BY department_id ORDER BY avg( salary ) LIMIT 1 )

9.3 查询平均工资最低的部门信息和该部门的平均工资
SELECT
d.*,
ag
FROM
department d
INNER JOIN ( SELECT avg( salary ) ag, department_id FROM employee GROUP BY department_id ORDER BY avg( salary ) LIMIT 1 ) ag_edp ON d.id = ag_edp.department_id;

9.4 查询平均工资最高的job信息
SELECT
j.*
FROM
job j
WHERE
j.id = (
SELECT
job_id
FROM
employee
GROUP BY
job_id
HAVING
avg( salary ) = (
SELECT
max(ag)
FROM
( SELECT avg( salary ) ag, job_id FROM employee GROUP BY job_id ) edp
)
);

或者
SELECT
j.*
FROM
job j
WHERE
j.id = ( SELECT job_id FROM employee GROUP BY job_id ORDER BY avg( salary ) DESC LIMIT 1 )

9.5 查询平均工资高于公司平均工资的部门有哪些
SELECT
avg( salary ),
department_id
FROM
employee
GROUP BY
department_id
HAVING
avg( salary ) > ( SELECT avg( salary ) FROM employee )

9.6 查询出公司中所有manager的信息
select * from employee where id in(select distinct manager_id from employee);

9.7 各个部门中,最高工资中最低的那个部门的最低工资是多少
SELECT
min( salary )
FROM
employee
WHERE
department_id = ( SELECT department_id FROM employee GROUP BY department_id ORDER BY max( salary ) LIMIT 1 );

9.7 查询每个专业的人数
select count(*),major_id from student group by major_id;

9.8 查询参加考试的学生中,每个学生的平均分,最高分
select avg(score),max(socre),sno from result group by sno;

9.9 查询姓张的每个学生的最低分大于60的学号,姓名
select s.sno,s.name,min(socre) from student s inner join result r on s.sno = r.sno where s.name like ‘张%’ group by s.no having min(score) > 60;

9.10 查询生日在‘1997-1-1’后的学生姓名,专业名称
select s.name,m.major_name from student s inner join major m on s.major_id = m.id where datediff(s.born_date,‘1997-1-1’) > 0;

9.11 查询每个专业的男生人数和女生人数是多少
select count(*) 个数,sex, major_id from student group by major_id,sex;

或者

SELECT
s.major_id,
( SELECT count( * ) FROM student WHERE sex = ‘男’ AND major_id = s.major_id ) 男生个数,
( SELECT count( * ) FROM student WHERE sex = ‘女’ AND major_id = s.major_id ) 女生个数
FROM
student s
GROUP BY
s.major_id;

9.12 查询专业和张三一样的学生的最低分
SELECT
min( score )
FROM
result
WHERE
sno IN ( SELECT sno FROM student WHERE majord_id = ( SELECT major_id FROM student WHERE NAME = ‘张三’ ) )

9.13 查询大于60分的学生的姓名,密码,专业名
SELECT
s.NAME,
s.PASSWORD,
m.major_name
FROM
student s
INNER JOIN major m ON s.major_id = m.id
INNER JOIN result r ON s.sno = r.sno
WHERE
r.soccre > 60;

9.14 按照邮箱位数分组,查询每组的学生个数
select count(*),length(email) from student group by length(email);

9.15 查询哪个专业没有学生
SELECT
m.id,
m.major_name,
s.sno
FROM
major m
LEFT JOIN student s ON m.id = s.major_id
WHERE
s.sno IS NULL;

或者
SELECT
m.id,
m.major_name,
s.sno
FROM
student s
RIGHT JOIN major m ON s.major_id = m.id
WHERE
s.sno IS NULL;

9.16 查询没有成绩的学生人数
SELECT
count( * )
FROM
student s
LEFT JOIN result r ON s.sno = r.sno
WHERE
r.id IS NULL;

10)联合查询
格式
select语句
union
select语句
union
select语句

特点:
1)多条查询语句的所查询的列必须一致
2)要求多条查询语句的查询的每一列的类型和顺序最好一致
3)union关键字默认去重,如果我们不希望去重,可以使用union all
10.1 例子1

select * from employee where email like ‘%a%’ or department_id = 7;

用联合查询

select * from employee where email like’%a%’
union
select * from employee where department_id = 7;

10.2 亦可以查询不同表的数据合并起来
select id,name,sex from t_ch where sex = ‘男’
union
select f_id,f_name,f_sex from t_fr where f_sex = ‘male’;

11)DML语言(数据操纵语言)

11.1 插入
1)方式一
语法:
insert into 表名(列名1,列名2…) values(值1,值2…);
注意:列名的个数和值的个数必须一致,如果想赋值空值,可以直接传null
如果没有列名,则需要按照列名的顺序,进行传值

1.1
insert into student(sno,name,email,grade_id,age,sex) values(‘2021003’,‘小蓝’,‘xiaolan@qq.com’,3,17,1);

1.2
insert into student(sno,name,email,grade_id,age,sex) values(‘2021004’,‘小南’,null,3,17,0);

1.3
insert into student values(‘2021005’,‘小九’,null,3,17,null);

2)方式二
语法:
insert into 表名
set 列名 = 值,列名 = 值…

2.1
insert into student set sno = ‘2021007’,name = ‘大白’;

3)以上两种方式的区别
3.1 方式一可以插入多条数据,而方式二不行
INSERT INTO student
VALUES
( ‘2021008’, ‘唐三’, NULL, NULL, NULL, NULL ),
( ‘2021009’, ‘小舞’, NULL, NULL, NULL, NULL ),
( ‘2021009’, ‘宁荣荣’, NULL, NULL, NULL, NULL );

3.2 方式一可以结合子查询来使用,方式二不支持
insert into student(sno,name,email)
select ‘2021111’,‘奥斯卡’,‘aosika@163.com’;

11.2 修改

单表修改语法:
update 表名
set 列名1 = 值1,列名2 = 值2…
where + 筛选条件

1.修改单表的记录
update student set name = ‘小胖’,email = ‘xiaopang@!63.com’ where sno = ‘2021111’;

多表修改语法:
update 表名1 别名
left | right | inner join 表名2 别名
on 连接条件
set 列 = 值…
where 筛选条件

2.修改多表的记录
2.1 修改张三的女友的手机号为117
UPDATE boy b
INNER JOIN beauty be ON b.id = be.boy_id
SET be.phone = ‘177’
WHERE
b.NAME = ‘张三’

2.2 修改没有男朋友的女生的男友编号为2
UPDATE boy b
RIGHT JOIN beauty be ON b.id = be.boy_id
SET be.boy_id = 3
WHERE
b.IS IS NULL;

11.3 删除

1.方式一:delete
1.1 单表删除
删除学号为2021111的学生
delete from student where sno = ‘2021111’;

1.2 多表删除
语法:
delete 表1的别名,表2的别名
from 表1 别名
inner | left | right join 表 2别名
where 筛选条件;

1.2.1 删除张三的女友信息
delete be from beauty be inner join boy b on b.id = be.boy_id where b.name = ‘张三’;

1.2.2 删除李四的信息以及其女友信息
delete be,b from beauty be inner join boy b on b.id = be.boy_id where b.name = ‘李四’;

2.方式二:truncate
语法:
truncate table 表名

3.方式一和方式二的区别
3.1方式一可以加筛选条件,方式二不行
3.2 truncate删除,效率高一点
3.3 假如要删除的表中有自增的列,如果用delete删除数据,再插入数据的话,自增列的值,从断点开始,而truncate删除后,自增列的值从1开始
换一种说法,就是truncate实际上就是把这个表数据和这个表都删除了,重新再建的一个新表
3.5 delete删除有返回结果,truncate删除没有返回结果
3.6 delete可以回滚,truncate不可以回滚

2.1 例子
truncate table student;

12)DDL(数据定义语言)
1.库的创建
create database if not exists 数据库名;

2.库名的修改,不支持,也不建议,容易丢失数据
rename database 旧库名 to 新库名;

3.更改库的字符集
alter database 库名 character set gbk;

4.库的删除
drop database if exists库名

5.表的创建
语法:
create table 表名(
列名1 列的类型【(长度)约束】,
列名2 列的类型【(长度)约束】,
列名3 列的类型【(长度)约束】,

列名n 列的类型【(长度)约束】

create table if not exists book(
id int,
name varchar(20),
price double,
author_id int,
publish_date datetime
)

create table if not exists author(
id int,
name varchar(20),
nation varchar(20)

)

6.表的修改
1)修改列名,column可以省略
alter table book change column publish_date pub_date datetime;

2)修改列的类型或者约束
alter table book modify column pub_date timestamp;

3)添加新列
alter table book add column annual double;

4)删除列
alter table book drop column annual;

5)修改表名
alter table book_author rename to author;

7.删除表
drop table if exists book_author;

8.表的复制
1)仅仅复制表的结构
create table copy_author like author;

2)复制表的结构和数据
create table copy2_author select * from author;

3)复制表的部分数据
create table copy4_author select id,name,nation from author where nation = ‘CN’;

4)仅仅复制表的某些字段
create table copy3_author select id,name from author where 1 = 2;

5)将department的数据插入到新表dept2(可以跨库复制表)
create table dept2 select id,name from test.department;

12)mysql中的数据类型
1.整型
1)分类:
tinyint(1个字节)
有符号-128-127
无符号0-255
smallint(2个字节)
有符号-32768-32767
无符号0-65535
mediumint(3个字节)
有符号-8388608-8388607
无符号0-1677215
int/integer(4个字节)
有符号-2147483648-2147483647
无符号0-4294967295
bigint(8个字节)
有符号-9223372036854775808-9223372036854775807
无符号0-9223372036854775807*2+1

2)特点:
2.1)如果不设置无符号还是有符号,默认是有符号的,如果想要设置无符号,需要添加unsigned关键字
create table test1(
id1 int,
id2 int unsigned
)
2.2)如果插入的值超出了整形的范围,会报out of range 异常,并且插入临界值

2.3)如果不设置长度,会有默认的长度,长度代表了显示的最大宽度,如果不够会使用0来往左边填充,但必须搭配zerofill来使用
create table test2(
id1 int(2) zerofill,
id2 int(9) zerofill
)
在这里插入图片描述

2.小数
1)分类
1.1)浮点型
float(m,d)
double(m,d)
1.2)定点型
dec(m,d)
decimal(m,d)

2)特点:
2.1)m和d是可以省略的
如果是decimal,m默认是10,d默认是0,
如果是float和double,则会根据插入的数值的精度来决定精度
create table test2(
f1 float,
f2 double,
f3 decimal
)
在这里插入图片描述

2.2)m:代表小数个数+整数个数
d:代表小数个数
如果超出范围,则插入临界值

3.字符型
1)较短的文本:
char
varchar

其他:
binary和varbinary用于保存较短的二进制

2)较长的文本:
text
blob(较长的二进制)

3)特点:
3.1)char(m),存储固定长度的字符
m代表最大的字符数,可以省略,默认为1,比较耗费空间,但效率高
3.2)varchar(m),存储可变长度的字符
m代表最大字符数,不可以省略,比较节省空间,但效率低

4)enum枚举,对于某个字段已经确定有哪几个值的了,就可以使用枚举
create table test2(
t1 enum(‘a’,‘b’,‘c’,‘d’)
)

在这里插入图片描述
5)set类型
create table test2(
t1 set(‘a’,‘b’,‘c’,‘d’)
)
desc test2;

insert into test2 values(‘a,b,c’);
在这里插入图片描述

4.日期型
1)分类
1.1)date只保存日期
1.2)time只保存时间
1.3)year只保存年
1.4)datieme保存日期+时间
1.5)timestamp保存日期+时间

2)特点:
2.1)datetime,占8个字节,范围1000-9999,不受时区的影响
2.2)timestamp,占4个字节,范围1970-2038,受时区的影响

create table test2(
d1 datetime,
d2 timestamp
)
desc test2;
insert into test2 values(now(),now());

在这里插入图片描述

13)约束
1.约束的概念?
一种限制,用于限制表中的数据,为了保证表中的数据的准确性和可靠性

2.约束的六大分类
1)NOT NULL,非空,用于保证该字段的值不能为空
2)DEFAULT,默认,用于保证该字段有默认值
3)PRIMARY KEY,主键,用于保证该字段具有唯一性,且非空
4)UNIQUE,唯一,用于保证该字段的唯一性,可以为空
5)CHECK,检查约束,mysql中不支持,用于检查值,比如年龄,性别这些
6)FOREIGN KEY,外键,用于限制两个表的关系,用于保证该字段的值必须来自于主表的关联列的值,在从表添加外键约束,用于引用主表中某列的值

3.约束的添加分类
1)列级约束,创建表时,在列后面加约束
语法上,6大约束都支持,但是如果设置外键还有check,其不会起效果
create table major(
id int primary key,
name varchar(10)
)

create table student(
id int primary key,
name varchar(20) not null,
gender char(1) check(gender = ‘男’ or gender = ‘女’),
seat int unique,
age int default 18,
major_id int references major(id)
)

2)表级约束,创建表时,所有列添加完成后,再加约束
除了非空,默认,其他的都支持
语法:【constraint 约束名】 约束类型(字段名)
注意:【constraint 约束名】可以省略
create table student(
id int,
name varchar(20) ,
gender char(1),
seat int ,
age int ,
major_id int,
constraint pk PRIMARY KEY(id),
constraint uq UNIQUE(seat),
constraint ck CHECK(gender = ‘男’ or gender = ‘女’),
constraint fk FOREIGN KEY(major_id) REFERENCES major(id)

);
show index from student;
在这里插入图片描述
4.(PRIMARY KEY)主键和(UNIQUE)唯一两种约束的对比
1)主键
能保证字段的唯一性,不允许为空,一个表中至多有一个主键,允许组合使用,比如PRIMARY KEY(id,name),这样在添加数据时,就会报id和name两个值的数据合为主键了。
2)唯一
能保证字段的唯一性,允许为空,一个表允许多个字段使用唯一约束,允许组合使用,但是注意,在插入第一条使用了unique约束的字段为空时的数据,再插入第二条该字段还是为空的就会报错了。如下字段seat加了唯一约束,插入第二条时就会报错
在这里插入图片描述
5.(FOREIGN KEY)外键的特点
1)要求在从表设置外键关系
2)从表外键列的类型要和主表关联列的类型要一致或者兼容,名称无要求
3)主表的关联列必须是一个key(主键约束或者唯一约束)
4)插入数据时,先插入主表,再插入从表
删除数据时,先删除从表,再删除主表

6.修改表时添加约束
添加主键
alter table book modify column id int primary key;
添加非空约束
alter table book modify column name varchar(20)not null;
添加默认约束
alter table book modify column price double default 20;
添加唯一约束
alter table book add unique(author_id);
alter table book modify column author_id int unique;
添加外键,constraint fk_st_maj可以省略
alter table book add constraint fk_st_maj foreign key(author_id) references author(id);

7.修改表时,删除约束
删除原有的默认约束
alter table book modify column price double;
删除主键·
alter table book drop primary key;
删除外键
alter table book drop foreign key fk_st_maj;

14)TCL(事务控制语言)
1.事务概念
事务:一个或者一组sql语句组成一个执行单元,这个执行单元要么全部执行,要么全部不执行

2.事物的acid属性
1)原子性,原子性是指事务是一个不可分割的工作单位,事务中的操作要么都执行,要么都不执行
2)一致性,一个事务执行会使数据从一个一致状态切换到另外一个一致状态
3)隔离性,一个事务的执行不应该受到其他事务的干扰
4)持久性,一个事务一旦提交,则会永久地改变数据库的数据

2.事务的创建
1)隐式事务
事务没有明显的开启和结束的标志,insert,update,delete等一条语句,就代表着一个事务

2)显式事务
事务有明显的开启和结束的标志,前提:必须把自动提交功能改为禁用

显示事务语法:
set autocommit = 0;
start transaction;可选的,可以写,也可以不写
sql语句1;
sql语句2…
commit; 提交事务
rollback; 回滚事务

例子1:
set autocommit = 0;
start transaction;
update book set price = 500 where id = 1;
update book set price = 1500 where id = 2;
commit;
在这里插入图片描述

例子2回滚:
set autocommit = 0;
start transaction;
update book set price = 1000 where id = 1;
update book set price = 1000 where id = 2;
rollback;
在这里插入图片描述

3.对于多个事务同时访问数据库相同数据时,遇到的并发问题
1)脏读
对于两个两个事务T1,T2,T1读取了T2更新但是没有提交的字段,之后,若是T2回滚了,T1读取的内容就是临时无效的
2)不可重复读
对于两个事务T1,T2,T1读取了一个字段,然后T2更新了该字段之后,T1再次读取同一个字段,值就不同了
3)幻读
对于两个事务T1,T2,T1从一个表中了读取了一个字段,然后T2在该表插入了一些新的行,之后,如果T1再次读取这个表的数据,就会多出几行。

4)数据库的隔离级别
oracle支持的2种事务隔离级别,read commited,serializable,oracle默认的隔离级别为read commited

mysql支持4中事务隔离级别,read uncommited(读未提交),read commited(读已提交),repeatable read(可重复读),serializable(串行化),mysql默认的隔离级别为repeatable read

read uncommited
允许事务读取未被其他事务提交的变更,脏读,重读读,幻读问题都会出现

read commited
只允许事务读取已经被其他事务提交的变更,可以避免脏读,但是重复读,幻读会出现

repeatable read
确保事务可以多次从一个字段中读取相同的值,在这个事务持续期间,禁止其他事务对这个字段进行更新,可以避免脏读,重复读,幻读会出现

serializable
确保事务可以从一个表中读取相同的行,在这个事务持续期间,禁止其他事务对这个表进行插入,更新,删除操作,所有并发的问题都可以解决,但是性能非常低

查看当前的隔离级别
select @@transaction_isolation;
在这里插入图片描述
设置当前mysql连接的隔离级别
set transaction isolation level read commited;

设置全局的mysql连接的隔离级别
set global transaction isolation level read commited;

5)回滚点savepoint,记得给回滚点起别名,回滚时用到这个别名
在这里插入图片描述
set autocommit = 0;
start transaction;
delete from book where id = 1;
savepoint a;
delete from book where id = 2;
rollback to a;
在这里插入图片描述

15)视图
1.视图的含义
mysql5.0.1版本开始提供视图功能,一种虚拟存在的表,行和列的数据来自定义视图的查询中使用的表,并且是在使用视图时动态生成的,只保存sql逻辑,不保存查询结果。

应用场景:
1)多个地方用到同样的查询结果
2)该查询结果使用的sql语句比较复杂

优点:
1)重用sql语句
2)简化复杂的sql操作,不必知道它的查询细节
3)保护数据,提高安全性

2.视图的创建
语法:
create view 视图名
as
查询语句;

1)查询姓名中包含“小”的员工名,部门名,工种名
create view myv1
as
select e.first_name,d.name,j.title
from employee e
inner join department d on e.department_id = d.id
inner join job j on e.job_id = j.id;
在这里插入图片描述

select * from myv1 where first_name like ‘%小%’;
在这里插入图片描述
2)查询各部门的平均工资级别
create view myv2
as
select avg(salary) ag,department_id
from employee group by department_id;

select myv2.ag,jg.level from myv2
inner join job_grade jg on myv2.ag between jg.lower and jg.higher;

3)查询平均工资最低的部门信息
select id,name from department where id = (select department_id from myv2 order by myv2.ag limit 1)

4)创建一个视图,查询员工的所有信息
create view myv2
as
select * from employee;

5)创建一个视图,要求查询部门的最高工资大于7000的部门信息
方法一:没用视图
select d.*,m.mx from department d
inner join(
select max(salary) mx,department_id from employee group by department_id having mx > 7000
) m on d.id = m.department_id;

方法二:使用视图
create or replace view myv3
as
select max(salary) mx,department_id
from employee group by department_id
having mx > 7000;

select d.*,m.mx from department d
inner join myv3 m on d.id = m.department_id;

3.视图的修改
1)方式一:
create or replace view myv2
as
select avg(salary) ag,department_id
from employee group by department_id;
2)方式二:
语法:
alter view 视图名
as
查询语句;

alter view myv2
as
select avg(salary) ag,department_id
from employee group by department_id;

4.视图的查看和删除
删除语法:
drop view 视图名,视图名…;

drop view myv1,myv2;

查看视图:
desc myv1;

show create view myv1;

5.视图的更新
注意:对视图的数据进行操作,也会影响到本身的表
create view myv1
as
select first_name,email from employee;
1)插入数据
insert into myv1 values(‘王’,‘wang@qq.com’);

2)更新数据
update myv1 set first_name = ‘张’ where first_name = ‘王’;

3)删除数据
delete from myv1 where first_name = ‘张’;

4)具备以下特点的视图不允许更新
4.1)包含以下关键字的sql语句,分组函数,distinct,group by,having,union或者union all
4.2)常量视图
在这里插入图片描述

4.3)select中包含子查询
4.4)join
4.5)from一个不能更新的视图
4.6)where子句的子查询使引用了from子句中的表

5)视图和表的对比
5.1)语法上,表时create tabel,试图是create view
5.2))空间上,表是实际上占用了内存空间,视图只是保存了sql语句的逻辑,仅仅占用了很小的内存空间
5.3)操作上,基本一致,都允许增删改查,但是视图一般都不允许

16)变量
1.系统变量,变量由系统提供,不是用户定义,属于服务器层面
1)系统变量-全局变量(global),作用域,服务器每次启将为所有全局变量初始化值,针对于所有的会话(连接),但不能跨重启,比如说我们改了某一个全局变量的值,再次重启的话,那个值还会恢复原来的值,除非我们去该配置文件里面的内容

2)系统变量-会话变量(session),作用域,仅仅针对于当前会话(连接)有效

3)查看所有的系统变量,如果不加global,那么默认就是session
show global variables;#全局的
show session variables;#会话的
4)查看满足条件的系统变量
show global variables like ‘%char%’;
5)查看指定的某个系统变量的值
select @@global.transaction_isolation;
6)为某个系统变量赋值
方式一:
set global | session 系统变量名 = 值;
set global autocommit = 0;

方式二:
set @@global | session.系统变量名 = 值;
set @@global.autocommit = 0;

2.自定义变量,变量由用户自定义,不是系统提供的
1) 自定义变量-用户变量,作用域,针对于当前会话(连接),应用在任何地方,begin end里面可以,begin end后面也可以。注意:声明变量时,必须同时也要赋值

1.1)创建变量三种方式
方式一:set @用户变量名 = 值;
方式二:set @用户变量名 := 值;
方式三:select @用户变量名 := 值;

1.2)更新变量值的四种方式
方式一:set @用户变量名 = 值;
方式二:set @用户变量名 := 值;
方式三:select @用户变量名 := 值;
方式四:select 字段 into @用户变量名 from 表;

1.3)查看用户变量
select @用户变量名;
在这里插入图片描述

2)自定义变量- 局部变量,作用域,仅仅在定义它的begin end内有效,应用在begin end中的第一句话!!!!!!
2.1)声明
declare 变量名 类型;
declare 变量名 类型 default 值;

2.2)赋值
方式一:set 局部变量名 = 值;
方式二:set 局部变量名 := 值;
方式三:select @局部变量名 := 值;
方式四:select 字段 into 局部变量名 from 表;

2.3)使用
select 局部变量名;

3.练习,定义几个变量,求和
在这里插入图片描述

17)存储过程
1.存储过程的含义
一组预先编译好的sql语句的集合,理解成批处理语句
好处:
1)提高代码的重用性
2)简化操作
3)减少了编译次数,并且还减少了和数据库连接的次数,提高效率

2.创建语法
create procedure 存储过程名(参数列表)
begin
存储过程体(一组合法的sql语句)
end

注意:
2.1)参数列表由三部分组成,参数模式 参数名 参数类型
举例:
in name varchar(20)

参数模式:
in:该参数可以作为输入,也就是该参数需要调用方传入值
out:该参数可以作为输出,也就是该参数可以作为返回值
inout:该参数既可以作为输入,也可以作为输出,也就是该参数即需要传入值,也可以返回值

2.2)如果存储过程体只有一句话,那么begin end可以省略,存储过程中的每条sql语句结尾要求必须加分号,存储过程的结尾可以使用delimiter 重新设置
语法:
delimiter 结束标志

delimiter $

3.调用语法
call 存储过程名(实参列表);

4.存储过程演示
4.1)空参的存储过程
创建
delimiter $
create procedure myp1()
begin
insert into user(user_name,password)
values(‘小红’,123),(‘小白’,123456),(‘小黑’,1234),(‘小兰’,12356),(‘小蓝’,1231);
end $

调用
call myp1()$;

结果
在这里插入图片描述

4.2)带in模式的存储过程
例子1根据员工名获取部门的信息
delimiter $
create procedure myp2(in firstName varchar(20))
begin
select d.*
from department d
right join employee e on d.id = e.department_id
where e.first_name = firstName;
end $

在这里插入图片描述

例子二,根据传入的用户名和密码,判断用户是否存在
创建存储过程
在这里插入图片描述

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

4.3)带out模式的存储过程
例子一,根据员工id,获取其对应的部门名称
创建
在这里插入图片描述

调用
在这里插入图片描述

例子二:根据员工id,获取部门表中的name和location_id这两个字段
创建
在这里插入图片描述

调用
在这里插入图片描述

4.4)带inout模式的存储过程
创建
在这里插入图片描述

调用
在这里插入图片描述

5.存储过程练习
1)创建存储过程,实现往用户表插入一数据
在这里插入图片描述

2)创建存储过程,传入员工id,获取员工姓名和工资
在这里插入图片描述

3)创建存储过程,用于比较两个员工的入职时间,比较大小
在这里插入图片描述
4)创建存储过程,实现传入一个日期,返回xx年xx月xx日的字符串
在这里插入图片描述

5)传入员工姓名,返回一个字符串:姓名 AND 部门名
在这里插入图片描述

6)创建存储过程,根据传入的条目数和起始索引,查询员工表的记录
在这里插入图片描述

在这里插入图片描述

6.存储过程的删除,注意:只允许一个一个的删除,不像视图可以删除多个
语法:drop procedure 存储过程名称;
drop procedure myp4;

7.存储过程的查看
语法:show create procedure 存储过程名
show create procedure myp5;

18)函数
1.函数的含义
一组预先编译好的sql语句的集合,理解成批处理语句
好处:
1)提高代码的重用性
2)简化操作
3)减少了编译次数,并且还减少了和数据库连接的次数,提高效率

2.函数和存储过程的区别
存储过程:可以有0个结果返回,也可以返回多个结果,适合做批量更新,批量更新
函数:有且只有一个结果返回,适合于处理数据后返回一个结果

3.创建语法:
create function 函数名(参数列表)returns 返回类型
begin
函数体
end

注意:
1)参数列表包含两部分
参数名和参数类型
2)函数体肯定会有return语句,如果没有那么就会报错,return不放在最后面也不会报错,不过不建议这样
3)函数体如果只有一句话,那么begin end可以省略
4)可以使用delimiter结束标志

4.调用函数
select 函数名(参数列表)

5.函数案例一:无参有返回
在这里插入图片描述
6.案例二:有参有返回
在这里插入图片描述
7.案例三:有参有返回
在这里插入图片描述

8.函数的查看
show create function 函数名;

9.函数的删除
drop function 函数名;

10.练习:传入两个float参数,返回其两者之和
在这里插入图片描述
19)流程控制结构
1.分类
顺序结构:程序从上往下执行
分支结构:程序从多条语句中,选择一条去执行
循环结构:程序再满足一定条件的基础上,重复执行一段代码

2.分支结构介绍
1)if函数
语法:
if(表达式1,表达式二,表达式三)
如果表达式一成立,则返回表达式二的值,否则返回表达式三的值
可应用在任何地方

2)case结构,
注意:如果then后面是语句,需要加上分号
2.1)情况一,类似于java的switch case,常用于等值判断
语法
case 变量|字段|表达式
when 要判断的值 then 返回的值1 或者语句1;
when 要判断的值 then 返回的值2 或者语句2;

else 要返回的值n 或者语句n;
end case;如果是语句为end case;如果是值为end

2.2)类似于java的多重的if语句,常用于区间判断
case
when 要判断的条件1 then 返回值1 或者语句1;
when 要判断的条件2 then 返回值2 或者语句2;

else 返回值n 或者语句n;
end case;

特点:
1.case语句也可以作为独立的语句使用,但前提必须在begin end里面
2.如果where中的条件满足,则执行对应的then后的语句,如果都满足,则执行else后的语句
3.else可以省略,else省略的情况下如果where都不成立,则会返回null

4.练习
在这里插入图片描述
3)if结构,可以实现多重分支
语法:
if 条件1 then 语句1;
elseif 条件2 then 语句2;

[else 语句n]
end if;

应用场景:
只允许运用在begin end中

例子:
在这里插入图片描述
4)循环结构,包含三大类,while,loop,repeat
1.循环控制条件
iterate类似于continue,继续,结束本次循环,进入下一轮循环
leave类似于break,结束整个循环

2.while,类似于java的while循环
语法:
【标签:】while 循环条件 do
循环体;
end while【标签】;

3.loop,因为没有循环控制条件,所以可以用来模拟死循环
语法:
【标签:】loop
循环体;
end loop【标签】;

4.repeat,类似于java中的do-while,无论如何都会执行一次
语法:
【标签:】repeat
循环体
until 结束循环的条件
end repeat【标签】;

5.练习1:批量插入数据
在这里插入图片描述
6.练习2:批量插入数据,如果次数大于2就停止插入数据,结合leave
在这里插入图片描述
7.练习3:批量插入数据,只插入偶次数数据,使用iterate
在这里插入图片描述

8.往表里插入指定个数的随机字符串
在这里插入图片描述

三、mysql高级部分
1.sql性能下降的原因?
1)查询语句写得不够好
2)索引失效,索引分为单值和复合值索引
3)关联表太多,用了太多的join
4)服务器调优及各个参数的设置(缓冲,线程数等)

2.sql的执行顺序
select distinct 字段
from 表 1
left | right | inner join 表2 on 连接条件
where 筛选条件
group by 字段
having 过滤条件
order by 排序
limit 分页

对于mysql而言它的执行顺序是
1)from 表1
2)on 连接条件
3)left|right|inner join 表2
4)where 筛选条件
5)group by 字段
6)having 过滤条件
7)select
8)distinct
9)order by 排序
9)limit

3.索引
1)索引的含义
索引(Index)是帮助mysql高效获取数据的数据结构,可以理解为排好序的快速查找数据结构

2)索引的优势劣势
2.1)优势
2.1.1)类似大学图书馆建书目索引,提高数据的检索效率,降低数据的IO成本
2.1.2)通过索引列对数据进行排列,降低数据排序的成本,降低了cpu的消耗

2.2)劣势
2.2.1)索引实际上也是一张表,该表保存了主键与索引字段,并指向实体表的记录,所以索引列也是占用空间的
2.2.2)虽然索引大大提高了查询速度,但是却会降低更新表的速度,如对表进行inset,update,delete操作,因为更新表时,不仅仅要保存数据,还要保存一下索引文件每次更新添加了索引列的字段,都会调整因为更新所带来的键值变化后的信息
2.2.3)索引只是提高查询的一个因素,如果mysql中有大量的数据,就需要花时间去研究建立最优秀的索引,或者优化查询

3)索引的分类
3.1)单值索引,即一个索引只包含单个列,一个表可以有多个单值索引
3.2)唯一索引,索引列的值必须唯一,但允许有空值
3.3)复合索引,即一个索引包含多个列

4)索引的基本语法
4.1)创建
create 【unique】index 索引名称 on 表名(字段名(字段长度),…)

alter table 表名 add 【unique】 index【索引名称】on (字段名(字段长度))

4.2)删除
drop index【索引名称】 on 表名;

4.3)查看
show index from 表名;

5)常用的四种方式来添加索引
5.1)alter table 表名 add primary key (字段名) 添加主键,值唯一
5.2)alter table 表名 add unique 索引名称(字段名)添加唯一索引,值允许为空
5.3)alter table 表名 add index 索引名称(字段名)添加普通索引,索引值可出现多次
5.4)alter table 表名 add fulltext 索引名称(字段名)用于全文搜索

6)mysql的索引结构
6.1)BTree索引(mysql中索引默认使用这种结构)
6.2)Hash索引
6.3)full-text全文索引
6.4)R-Tree索引

7)什么情况下建索引,什么情况下不建索引?
7.1)建索引的情况下

7.1.1)主键自动建立唯一索引
7.1.2)频繁作为查询的字段应该加索引
7.1.3)查询中,与其他关联表的字段,外键关系建立索引
7.1.4)频繁更新的字段不适合建索引
7.1.5)where条件用不到的字段不要建索引
7.1.6)单值、复合选哪一种?高并发的情况下,选择复合
7.1.7)查询中排序的字段,排序字段若通过索引去访问将大大提高排序速度
7.1.8)查询中统计或者分组字段

7.2)不建索引的情况下
7.2.1)表记录太少
7.2.2)经常做增删改的表
7.2.3)数据重复且分布平均的字段,因此只为经常查询和经常排序的字段加索引

8)explain
8.1)explain含义
使用explain关键字可以模拟优化器执行sql语句,从而知道mysql时如何处理你的sql语句的,分析你的查询语句或者表结构的性能瓶颈

另外explain一条select语句,会显示如下内容:
在这里插入图片描述

8.2)explain之id
select 查询的序列号,包含一组数字,表示查询中执行select子句或者操作表的顺序
分三种情况:

8.2.1)id全都相同,执行顺序由上至下
8.2.2)id全都不同,如果是子查询,id的序号会递增,id值越大,优先级就越高,越先被执行
8.2.3)id有些相同,有些不同的情况,id如果相同,那么可以认为它们是一组的,执行顺序由上至下,在所有组中,id越大的,优先级越高,越先被执行

8.3)explain之select_type

8.3.1)SIMPLE,简单的select语句,查询中不包含子查询和union
8.3.2)PRIMARY,查询中若包含任何复杂的子查询部分,最外层则被标志为PRIMARY
8.3.3)SUBQUERY,在select或者where包含了子查询
8.3.4)DERIVED(衍生),在from列表中包含的子查询被标志位DERIVED(衍生),mysql会递归执行这些子查询,把结果放在临时表里
8.3.5)UNION,若第二个select语句出现在UNION之后,则被标志为UNION,若UNION包含在from子句的查询中,外层select被标志为DERIVED
8.3.6)UNION RESULT,从UNION表获取结果的select语句

8.4)explain之type
type意为访问类型,是较为重要的一个指标,一般查询来说,至少达到range,最好达到ref
以下最好到最差的顺序是?
system>const>eq_ref>ref>range>index>all

8.4.1)system
表中只有一条记录,等于系统表,这是const类型的特例,平时不会出现,一般忽略不计

8.4.2)const
表示通过索引一次就找到了,const用于比较primary key和唯一索引,因为只匹配一行数据,所以很快。如将主键置于where列表中,mysql就能将改查询转换为一个常量

8.4.3)eq_ref
唯一索引扫描,对于每个索引键,表中只有一条记录与之匹配,常见于主键或者唯一索引扫描

8.4.4)ref
非唯一索引扫描,返回匹配某个单独值的所有行,本质上也是一种索引访问,它返回所有匹配某个单个值行,然而,它可能会找到多个符合条件的行,所以它属于查找和扫描的混合体

8.4.5)range
只检索给定范围的行,使用一个索引来选择行。key列显示使用了哪个索引,一般就是在你的where语句中出现了between,<,>,in等的查询,这种范围扫描索引比全表扫描要好,因为它需要开始于索引的某一点,结束语索引的另一点,不用扫描全部索引

8.4.6)index
full index scan,index和all的区别是index类型只遍历索引树,这通常比all快,因为索引文件比数据文件小,也就是说虽然index和all虽然都是读全表,但index是从索引中读的,all是从硬盘中读的

8.4.7)all
full table scan,将遍历全表以找到匹配的行

8.4.8)NULL

8.5)explain之possible_keys
显示可能应用在这张表的索引,一个或者多个,查询涉及到字段上若存在索引,则该索引将被列出,但不一定被实际查询使用

8.6)explain之key
实际使用的索引,如果为NULL,则代表没有使用索引,查询中若使用了覆盖索引,则该索引仅出现在key列表中。何为覆盖索引?其又被称为索引覆盖,就是select的字段都已经建立了索引,其不必再去读取数据行,mysql会利用索引返回select列表中的字段,不必根据索引再次读取数据文件。

8.7)explain之key_len
表示索引中使用的字节数,可通过该列计算查询中使用的索引长度,在不损失精度的情况下,长度越短越好,key_len显示的值为索引字段的最大可能长度,并非实际使用长度,而key_len是根据表定义计算而得,不是通过表内检索而出的

8.8)explain之ref
显示索引的哪一列被使用了,如果可能的话,是一个常数,哪些列或者常量被用于查找索引列上的值

8.9)explain之rows
根据表统计信息及索引选用情况,大致估算出找到所需记录所需要读取的行数

8.10)explain之Extra
包含不适合在其他列中显示但十分重要的额外信息,这些信息很有可能导致sql执行得很慢

8.10.1)Using fileSort
说明mysql会对数据使用一个外部的索引排序,而不是按照表内的索引顺序进行读取,mysql中无法利用索引完成的排序,又称之为文件排序

如下,三个字段col1,clo2,col3加了复合索引,第一条语句,where后面只加了col1和col3这两个字段,所以就会出现using fileSort。而第二条语句where后面col1,col2,col3都加了,所以没出现。
在这里插入图片描述

8.10.2)using temporary
使用了临时表保存了中间结果,mysql在对查询结果进行排序时使用临时表,常见于order by和group by
在这里插入图片描述

8.10.3)using index
表示相应的select操作中使用了覆盖索引,避免访问了表的数据行,效率还不错。如果同时出现using where,表明索引被用来执行索引键值的查找,如果没有同时使用using where,表明索引用来读取数据并非执行查找动作
在这里插入图片描述

8.10.4)using where
表明使用了where过滤

8.10.5)using join buffer
表明使用了连接缓存

8.10.6)impossible where
表示where子句的值总是false,不能用来获取任何元组

8.10.7)select tables optimized away
在没有group by子句的情况下,基于索引优化min/max操作或者对于myisam存储引擎优化count(*)操作,不必等到执行阶段再进行计算,查询执行计划生成的阶段即完成优化

8.10.8)distinct
优化distinct操作,在找到第一匹配的元组后停止查找同样值的动作

9)单表优化案例
查询category_id为1,且comments大于1的情况下,views最多的article_id
在这里插入图片描述
9.1)如下为未优化的情况下,可以看到type为all,且出现了using fileSort,这种情况下都是极坏的,所以必须优化
在这里插入图片描述
9.2)加索引优化的情况下,结合三个字段加索引,发现type已经改为range了,这里还是不够妥当,还是出现了using fileSort,是为什么呢?因为comments>1是一个范围,不是一个常量,会导致后面的索引失效。
在这里插入图片描述
9.3)加索引优化的情况下,结合两个字段建索引完美解决问题
在这里插入图片描述

10)两表优化案例
注意:如果为left join的情况下,索引一般加载从表的字段上,为什么?因为left join 主表就必定全表扫描,加载主表没多大效果,right join的原理同上。

10.1)以left join为例子,shop为主表,book为从表,主表加索引的情况下,效率还是低
在这里插入图片描述

10.2)以left join为例子,在从表加索引的情况下,效率高
在这里插入图片描述

11)三表优化案例
11.1)未加索引的情况,效率极低
在这里插入图片描述
11.2)加了索引,效率提高了,注意:多表left join ,从表字段都要加索引,right join同理,如下,就为book和phone这两个从表的字段都加了索引
在这里插入图片描述

12)索引失效
12.1)索引失效的原因
12.1.1)全职匹配我最爱
正常情况
在这里插入图片描述
索引失效的情况,抛弃了第一个索引
在这里插入图片描述
索引失效的情况,跳过了中间索引,下图虽然是两个筛选条件,但还是只用了一个const
在这里插入图片描述

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

12.1.3)不在索引列上做任何操作(计算,函数,自动或者手动类型转换),会导致索引失效而去做全表扫描

正常情况下
在这里插入图片描述
多索引列做了相关操作后,索引就失效了
在这里插入图片描述

12.1.4)存储引擎不能使用索引中范围条件右边的列
建一个复合索引,顺序分别为name,age,email
如下
在这里插入图片描述

正常情况下
在这里插入图片描述

使用范围,出现了索引失效,最后面的那个索引失效
在这里插入图片描述

12.1.5)尽量使用覆盖索引(只访问索引的查询(索引列和查询的列一致)),减少select *

12.1.6)mysql中使用不等于(!=,<>)会使得索引失效,但是在mysql8.0依然有效
在这里插入图片描述

12.1.7)is null,is not null也会使得索引失效,但是在mysql8.0 is null的情况下,其还是有效
在这里插入图片描述

12.1.8)like以通配符%开头(%abcd…),会使得索引失效
结论:使用覆盖索引来解决此问题,覆盖索引,即是查询的字段和索引列相同

索引失效的情况
在这里插入图片描述
那么,非得要解决呢?如何解决?
没加索引的情况下
在这里插入图片描述

加了索引的情况下
在这里插入图片描述

加了索引,这些情况下会导致索引失效
在这里插入图片描述

12.1.9)字符串不加单引号会使索引失效,如下name字段加了索引
在这里插入图片描述

12.1.10)少用or,用它来连接时会出现索引失效,如下为mysql8.0其未失效
在这里插入图片描述

12.2)总结
![在这里插入图片描述](https://img-blog.csdnimg.cn/ed5a7b9eea8145198fba5ffca1b6c499.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MTM1OTI3Mw==,size_16,color_FFFFFF,t_70

12.3)索引面试题
12.3.1)建表以及插入数据
在这里插入图片描述
12.3.2)c1,c2,c3,c4建复合索引
在这里插入图片描述12.3.3)正常情况,索引都生效
在这里插入图片描述
12.3.4)从上面可以得出使用四个索引,长度为128,如下用到了>,所以只用到了三个索引
在这里插入图片描述

12.3.5)如下这种情况比较特殊,虽然c4条件在c3前面,但是mysql底层会进行索引排序,故顺序仍然为c1,c2,c3,c4,c4用了范围,代表c4之后的索引会失效
在这里插入图片描述

12.3.6)这种情况部分用于筛选,部分用于分组,其实如下这条explain显示只用到了两个索引,但实际上c3的索引也用到了,用在了排序上
mysql> explain select * from test where c1 = ‘a1’ and c2 = ‘a2’ and c4 = ‘a4’ group by c3;

12.3.7)这种情况结果和12.3.6一样
mysql> explain select * from test where c1 = ‘a1’ and c2 = ‘a2’ group by c3;

12.3.8)这种情况,会导致性能下降,出现using fileSort,使用了外部排序,理由是直接跳过了c3
mysql> explain select * from test where c1 = ‘a1’ and c2 = ‘a2’ group by c4;

12.3.9)这种情况下,只用到了一个索引,其他索引用作了排序,没出现using fileSort
mysql> explain select * from test where c1 = ‘a1’ and c5 = ‘a5’ group by c2, c3;

12.3.10)就12.3.9而言,如果调换了group by后的字段,会出现using fileSort
mysql> explain select * from test where c1 = ‘a1’ and c5 = ‘a5’ group by c3, c2;

12.3.11)对于12.3.9而言,如果同时出现了c1和c2条件,(此时c2已经为一个常量)且c2作为group by的字段其就不会出现using fileSort
mysql> explain select * from test where c1 = ‘a1’ and c2 = ‘a2’ and c5 = ‘a5’ group by c3, c2;

12.3.12)这种情况,只用到了一个索引,c2c3索引用作了排序
mysql> explain select * from test where c1 = ‘a1’ and c4= ‘a4’ group by c2, c3;

12.3.13)这种情况糟糕了,只用到了一个索引,出现了using temporary,using fileSort
mysql> explain select * from test where c1 = ‘a1’ and c4= ‘a4’ group by c3, c3;

12.3.14)总结
1)慢查询的开启以及捕获
2)explain 慢查询的sql语句并且分析
3)show profile 查询sql在mysql服务器里面执行的细节以及生命周期
4)sql服务器一些参数的调优

13)order by排序优化
建表以及索引
在这里插入图片描述

13.1)没有出现using fileSort的情况
在这里插入图片描述
在这里插入图片描述

13.2)出现using fileSort的情况,很糟糕!!!
在这里插入图片描述
在这里插入图片描述

13.3)order by 子句,尽量使用index(索引排序)方式排序,避免使用fileSort(文件排序),尽可能在索引列上完成排序操作,遵照索引建的最左前缀

13.4)如果不在索引列上,fileSort有两种算法:
mysql就要启动双路排序和单路排序

双路排序
1)mysql4.1之前是使用双路排序的,意思就是两次扫描磁盘,最终得到数据,读取行指针和order by列,对他们进行排序,然后扫描已经排好序的列表,按照列表中的值重新从列表中。

2)从磁盘取排序字段,在buffer进行排序,再从磁盘取其他字段

取一批数据,要对磁盘进行扫描,I/O是很耗时的,所以在mysql4.1之后,出现了一种改进的算法,即为单路排序

单路排序
从磁盘中读取查询所需要的列,按照order by列在buffer对它们进行排序,然后扫描排序后的列表进行输出,它的效率更快一些,避免了第二次读取数据,并且把随机I/O变成了顺序I/O,但是它会使用更多的空间,因为它把每一行都存在了内存中

单路排序可能出现的问题,注意!!!!!!
在排序缓存中,sort_buffer,方法B比A要占很多空间,因为方法B是要将所有字段都取出来,所以有可能取出来的数据的总大小超过了sort_buffer的容量,导致每次只能取sort_buffer容量大小的数据,创建temp临时文件,多路合并,排完序后,再取sort_buffer容量大小的数据,再排序…从而导致多次I/O的出现!!!消耗I/O成本。

13.5)如何提高order by的速度
1)尽量不要使用select *,这对于order by而言是一个大忌!因为这样查询数据很有可能出现查询的数据的超出sort_buffer容量,超出之后,会创建temp临时文件进行合并排序,导致多次I/O

2)尝试增大sort_buffer_size的值(参数调优)

3)尝试增大max_length_for_sort_data的值(参数调优)

13.6)总结
在这里插入图片描述

14)group by关键字优化(和order by类似)
1)group by实质上是排好序再分组,遵循索引建的最佳左前缀原则
2)当无法使用索引列时,可尝试增大sort_buffer_size和max_length_for_sort_data的值
3)where高于having,能在where加条件就尽量在where加

15)慢查询日志
1)含义
慢查询日志时mysql提供的一种日志记录,它用来记录在mysql中响应时间超过阙值的sql语句,具体是指运行时间超过long_query_time(开启之后,默认为10)的值,则会被记录到慢查询日志中

2)如何开启慢查询功能?
默认下,mysql关闭了慢查询的功能,因为开启的话,在性能方面带来一些影响,所以如果不是调优的话,一般不开启慢查询功能

查看是否开启慢查询功能,日志记录在.log文件中
在这里插入图片描述
开启慢查询功能,注意:只针对当前数据库连接生效,重启后会失效,如果要长久生效,只有去配置文件加!

在这里插入图片描述

长久生效!
在这里插入图片描述

3)修改慢查询的阙值
在这里插入图片描述
4)查看慢查询出来的语句的记录数
在这里插入图片描述
5)一个例子
在这里插入图片描述
在这里插入图片描述

6)日志分析工具,mysqldumpslow
mysqldumpslow --help;
在这里插入图片描述
如何使用?
在这里插入图片描述
日志所在的目录,每个人的可能都不同!
在这里插入图片描述

16)show profile
16.1)创建存储过程预先往表里插入50万条数据

设置参数,存储过程批量大量数据,要设置这个参数
在这里插入图片描述

创建存储过程
在这里插入图片描述

调用
在这里插入图片描述

16.2)show Profile的含义:是mysql提供可以用来分析当前会话中,语句执行的资源消耗情况,可以用于sql的调优

16.3)查询以及开启show Profile功能
在这里插入图片描述

16.4)查看资源消耗情况
1)查看sql语句query的id,duration代表所消耗时间,单位为秒
在这里插入图片描述
2)查看指定query_id对应的sql语句的生命周期
在这里插入图片描述

3)profile后面可接的参数
在这里插入图片描述

4)牢记使用show profile + 参数后,出现如下的这几个结果,跟sql的性能有着极大的关联!!!
在这里插入图片描述

17)全局查询日志,只能在测试环境用,千万不要在正式环境用!
17.1)开启全局查询日志
在这里插入图片描述

17.2)查询sql语句日志
在这里插入图片描述
18)mysql锁机制
18.1)锁的定义
锁是计算机协调多个线程或者进程并发访问某一资源的机制

18.2)锁的分类
1.从对数据的操作类型来分
1.1)读锁(共享锁)
针对同一份数据,多个读操作可以同时进行而不会互相影响

1.2)写锁(排他锁)
当前操作没有执行完成之前,它会阻断其他写锁和读锁

2.从对数据操作的粒度来分
2.1)表锁(偏读)
特点:偏向myisam引擎,开销小,加速快,无死锁,粒度大,发生锁冲突的概率最高,并发度最低

2.1.1)手动加表锁语法
lock table 表名1 read/write,表名字2 read/write…;

2.1.2)查看加了锁的表
在这里插入图片描述
2.1.3)给表加锁
在这里插入图片描述

2.1.4)解锁
在这里插入图片描述
2.1.5)加读锁 – 假设这里会话1给mylock表加了个读锁,会话1只能查加了读锁的表的数据,不能查其他表的数据,并且会话1不能更新这个表的数据,其他会话可以查所有表的数据,但是如果去更新那个加了锁的表数据会进入阻塞状态,要等到会话1解锁,才可以更新数据

会话1
在这里插入图片描述

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

会话2
在这里插入图片描述
2.1.6)写锁 – 假设会话1给mylock表加了个写锁,会话1只能获取或者更新mylock表的数据,并不能获取其他表的数据,其他会话只能获取到没有加写锁的表的数据,获取mylock表数据时会进入阻塞状态,只有当会话1将mylock表的锁解掉,其他会话才可以mylock表的数据
会话1
在这里插入图片描述

会话2
在这里插入图片描述

2.1.7)表锁分析
1)查看那些表被加锁了
show open tables;

2)如何分析表锁定
在这里插入图片描述
Table_locks_immediate :产生表级锁定的次数,表示可以立即获取锁的查询次数,没立即获取锁值加1

Table_locks_waited :出现表级锁定争用而发生等待的次数(不能立即获锁的次数,每等待一次锁值加1),此值高,则存在着较严重的表级锁争用情况

2.1.8)结论
myisam引擎在执行sql语句(select)时,会自动给涉及到的表加读锁,对进行增删改操作时,对涉及到的表加写锁。

除此之外,myisam的读写锁调度优先是写锁,这也是myisam不适合作写为主表的引擎,因为写锁后,其他线程不能进行任何操作,大量的更新会很难得到锁,从而造成永远阻塞

情况一:
对myisam表的读操作(加读锁),不会阻塞其他进程对同一个表的读请求,但会对同一个表的写操作进入阻塞,只有当读锁释放后,其他进程才可以进行写操作

情况二:
对myisam表的写操作(加读锁),会阻塞其他进程对同一个表的读操作和写操作,只有写锁释放后,其他进程才可以对同一个表进行读写操作

简而言之就是,读锁会阻塞写,但不阻塞读,而写锁会把读和写都堵塞

2.2)行锁(偏写)
特点:偏向innodb引擎,开销大,加锁慢,会出现死锁,锁定粒度最小,发生锁冲突的概率最低,并发度也很高

2.2.1)建表以及分别为a,b两个字段建索引
在这里插入图片描述
在这里插入图片描述

2.2.2)行锁基本演示(针对同一条数据),即两个会话,更新同一条数据,如果会话1更新数据,会话1可以查到它所更新的数据,但是其他会话查不到会话1更新的数据,而且其他会话更新同一行数据的时候会进入阻塞状态,只有当会话1commit事务后,才可以更新数据!!!

会话1
在这里插入图片描述
在这里插入图片描述

会话2

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

2.2.3)如果两个会话操作两条不同的数据的话,则不会出现这种阻塞情况,但是要查询出其他行数据更新后的变化,要等其他会话提交,才能获取到最新的值

会话1
在这里插入图片描述
在这里插入图片描述
会话2
在这里插入图片描述
在这里插入图片描述
2.2.4)索引失效导致行锁变表锁,如下可以看到,会话1字段a的类型是varchar的,但是where后面接的是数值类型,导致索引失效,即行锁变为了表锁,这就出现了即使会话1和会话2虽然不是操作同一条数据,但是会话2也进入了阻塞状态,
在这里插入图片描述

2.2.5)间隙锁
当我们用范围条件而不是相等条件检索数据,并请求共享或排他锁时,inndodb会给符合条件的已有数据记录的索引项加锁,对于键值在条件范围内但并不存在的记录,叫做间隙(GAP),innodb也会对这个间隙加锁,这种锁机制就是所谓的间隙锁(Next-key)锁

比如这条语句
update test_innodb_lock set a = ‘cc’ where id > 1 and id < 5;
假设id为2,3的数据都已经存在,但是id为4的对应的数据不存在,会话1即上述语句执行了更新操作,另外一个会话想要插入id为4的数据,其就会进入阻塞!!!

缺点:
当锁定一个范围键值之后,即使某些不存在的键值也会被无辜的锁定,而造成在锁定的时候,无法插入锁定键值范围内的任何数据,在某些场景下,这对性能造成很大的危害!!!

2.2.6)如何锁定某一行数据?先执行begin;再查询出一条数据加上for update,如下可以看到会话2进入了阻塞状态
在这里插入图片描述
会话1commit后,其他会话才能操作之前被锁住的那一行数据
在这里插入图片描述

2.2.7)行锁分析
在这里插入图片描述
参数说明:
Innodb_row_lock_current_waits :当前正在等待锁定的数量
Innodb_row_lock_time :从系统启动到现在锁定总时间长度
Innodb_row_lock_time_avg :每次等待所花平均时间
Innodb_row_lock_time_max :从系统启动到现在等待最长的一次所花的时间
Innodb_row_lock_waits :系统启动后到现在总共等待的次数

行锁优化建议:
1)尽可能让所有数据检索都通过索引来完成,避免无索引行锁升级为表锁
2)合理设计索引,尽量缩小锁的范围
3)尽可能减少检索条件,避免间隙锁
4)尽量控制事务大小,减少锁定资源量和时间长度
5)尽可能降低事务隔离级别

2.3)页锁
开销和加锁时间界于表锁和行锁之间,会出现死锁,锁定粒度界于表锁和行锁之间,并发度一般

19)mysql的主从复制

19.1)复制的基本原理,slave会从master读取binlog来进行数据同步
三个详细步骤:
1)master将改变记录到二进制文件(binary log),这些记录过程叫做二进制日志事件,binary log events

2)slave将master的binary log events拷贝到它的中继日志(relay log)

3)slave重做中继日志中的事件,将改变应用的自己的数据库中,mysql的复制是异步且串行化的

19.2)复制的基本原则
1)每个slave只能有一个master
2)每个slave只能有一个唯一的服务器ID
3)一个master可以有多个slave

19.3)复制的最大问题
延时

19.4)一主一从常见配置
1)mysql版本一致且后台以服务运行,主从机在同一个网段下,且互相ping得通,主从配置都在[mysqld]结点下,都是小写

2)主机(本地window系统装的mysql)修改my.ini文件
2.1)【必须】主服务器ID必须唯一
在这里插入图片描述

2.2)【必须】启用二进制日志
在这里插入图片描述

2.3)【可选】启用错误日志
在这里插入图片描述

2.4)设置不需要复制的数据库
binlog-ignore-db = 数据库名

2.5)设置需要复制的数据库
binlog-do-db = 数据库名

3)从机修改(liniux系统装的mysql)修改my.conf文件
3.1)【必须】从服务器ID唯一
在这里插入图片描述

3.2)【可选】开启二进制日志

在这里插入图片描述

4)主从机都关闭防火墙
主机关闭
在这里插入图片描述
在这里插入图片描述

从机关闭
在这里插入图片描述

5)在主机上建立账户并且授权给slave
创建账户,192.168.184.128为从机的ip地址
在这里插入图片描述

授权
在这里插入图片描述
查看master状态,注意两个参数,file和position,记住它们的值
在这里插入图片描述

File:代表哪个文件
Position:代表从第几行开始

6)在从机上配置需要复制的主机,master_host配置的是主机ip地址
在这里插入图片描述

7)启动从服务器复制功能
在这里插入图片描述
8)如下那两个参数的值均为YES则说明主从复制配置成功
在这里插入图片描述

9)演示
在主机建库,建表,插入数据
在这里插入图片描述
从机也会存在对应的库,表,数据
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值