MySQL数据库学习笔记--最全、最新

1、数据库简介

1.1 相关概念

DB:数据库(database):存储数据的仓库。保存了一系列有组织的数据

DBMS:数据库管理系统(Database Management System)。数据库是通过DBMS创建和操作的容器

SQL:结构化查询语言:专门用来与数据库通信的通信

1.2 特点

1、将数据放在表中,表在放在库中;

2、一个数据库可以有多个表,每个表有一个名字,用来标识自己,有唯一性;

3、表定义了一些特性,这些特性定义了数据在表中如何存储,类似于java中类的设计;

4、表由列组成,称为字段。所有表都有一个或多个列组成,类似于java中的属性;

5、表中的数据按行存储,每一行相当于java中的一个对象

1.3 MySQL的启动与登录

1.3.1 MySQL服务启动与停止

​ ①以管理员身份进入命令提示符

​ ②命令 net start MySQL 服务启动,MySQL为启动服务的名称

​ ③命令 net stop MySQL 服务停止

1.3.2 MySQL服务端登录与退出

登录方式一:

​ ① 以管理员身份进入命令提示符

​ ② 登录命令:MySQL【-h localhost -P 3306】 -u root -p 或者 MySQL 【-h localhost -P 3306】 -u root -p1234(密码) 直接进入

​ 注意:如果用于本机,【】内容可省

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sgyaquH3-1682066831846)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20211109174339695.png)]

登录方式二:

​ 通过MySQL自带的客户端,只限于root用户

退出方式:exit;或者ctrl + c

2、MySQL常见命令介绍

2.1 常见命令

常见命令 功能介绍 举例
show databases 展示数据库管理系统中存在的数据库
use 数据库名 进入某个数据库 use sys;
show tables 展示当前库中存在的表
show tables from 数据库名 展示某个数据库中的所有表 show tables from mysql;
select database() 查看当前所在库
creat table 表名(指定列名) creat table stuinfo(id int ,name varchar(20));
desc 表名 查看表的结构 desc stuinfo;
select * from 表名 查看表中的数据 select *from stuinfo;
insert into 表名(列名)values(数值) 在表中插入数据 insert into stuinfo (id,name)values(1,“Tom”);
update stuinfo set name = ‘lilei’ where id =1; 更新信息
delete from stuinfo where where id = 1; 删除数据
select version(); 查看数据库的版本

1、创建指定字符集的数据库:create database python01new charset=utf8;

2、查看数据库创建信息:show create database python01;

3、如果数据库名称中含有特殊字符,例如:python-04,加上python-04即可

4、查看数据库版本:

方式一:登录到mysql服务端 select version();

方式二:没有登录数据库,MySQL --version 或者 MySQL–V

2.2 书写规范

①不区分大小写,建议关键字大写,表名、列名小写

②每条命令最好用分号结尾

③每条命令可以根据需要,可以缩进和换行

④注释

​ 单行注释:#注释文字

​ 单行注释:-- 注释文字 (注意空格)

​ 多行注释:与java一样

2.3 数据类型

1、小数:decimal表示浮点类型,例如:decimal(5,2)表示共存五位数,小数占两位

2、日期类型:date、time、datetime

3、对于图片、音频、视频等文件,不存在数据库中,而是上传到某个服务器,然后在表中存储这个文件的保存路径

2.4 约束

1、主键primary key:物理上的存储顺序

2、非空not null:此字段不允许填写空值

3、唯一unique:此字段的值不允许重复

4、默认default:不填写时使用默认值,填写时以默认值为准

5、外键foreign key:对关系字段进行约束,当为关系字段填写值时,会到关联的表中查询此值是否存在,如果存在则填写成功,不存在则填写失败,抛出异常

6、注意:虽然外键约束可以保证数据的有效性,但是在进行数据的crud时,都会降低数据库的性能,所以不推荐使用,那么如何保证数据的有效性?答:可以在逻辑层面进行控制

3、DQL语言的学习

3.1 基础查询

3.1.1 语法

select 查询列表 from 表名;

3.1.2 特点

1、查询列表可以是:表中的字段、常量值、表达式、函数
2、查询的结果是一个虚拟的表格

3.1.3 SQL语句实现

① 查询表中的单个字段

SELECT last_name FROM employees;

② 查询表中的多个字段,字段顺序随机,与表中顺序无关

SELECT last_name,salary FROM employees;

③ 查询表中的全部字段

#方式一:
SELECT * FROM employees;
#方式二:
#类似于多个字段的查询

④ 查询常量值

SELECT 100;

⑤ 查询表达式

SELECT 100%98;

⑥ 查询函数

SELECT VERSION();

⑦ 起别名

使用:字段名 AS 别名,同时AS也可以用空格代替

优点:1、便于理解;2、如果查询的字段有重名的情况,可以使用别名区分开

注意:如果别名中含有特殊的字符,建议使用双撇号,单撇号也适用(不建议)

SELECT 100%98 AS 结果;
SELECT last_name AS 姓氏 FROM employees;
#案例:查询salary,显示结果为 out put  如果有特殊字符,建议将别名用双引号包住
SELECT salary AS "out put" FROM employees;

⑧ 去重 添加关键字 distinct(有区别的)

#案例:查询员工表中的涉及到的所有部门的编号
SELECT DISTINCT department_id FROM employees;

⑨concat函数的作用

#案例:查询员工名和姓连成一个字段,显示为:姓名
SELECT CONCAT(last_name,first_name) FROM employees;

注意:如果有一个字段的值为null,拼接结果全为 null

解决:使用函数 IFNULL,如果commission_pct为null,就会被替换为0

SELECT
	IFNULL(commission_pct, 0) AS 奖金率
FROM
	employees;

⑨补充

​ Java中"+"号的作用:

​ a:当两端都是操作数时,用作运算符;b:当"+"一端为字符串时,结果为字符串,用作连接符;c:字符串也可以使用concat方法连接

​ MySQL中"+"的使用:

​ a:其中一方为字符型,试图将字符型转换为数值型,如果成功,继续加法运算;假如失败,将字符型转换为数值0;

​ b:只要其中一方为null,结果肯定为null

SELECT '123'+90;      # 213
SELECT 'mary'+90;     # 90
SELECT  null+90;       #null

3.1.4 特别注意

① select部分,追加函数、常量值、表达式,如果有表示全部字段的 * 号,必须写在*后面

3.2 条件查询

3.2.1 语法

SELECT
			查询列表      最后执行
FROM
			表名          先执行
WHERE
			筛选条件;      再执行

3.2.2 分类

① 按条件表达式筛选

条件运算符:> < != <> >= <=

② 按逻辑表达式筛选 作用:用于连接条件运算符

逻辑运算符:&&(and) ||(or) !(not)

SELECT
	CONCAT(first_name,"的工资:",salary)
FROM
	employees
WHERE
	salary > 10000 && manager_id = 100;

③模糊查询 like、 between and、 in、 is null

​ 1、LIKE特点:

a:一般和通配符搭配使用【通配符:%代表任意多个字符,包含0个字符;_ 代表任意单个字符】

#案例1:查询员工名中包含字符a的员工信息
SELECT
	*
FROM
	employees
WHERE
	last_name LIKE '%a%';
#案例2:查询员工名中第三个字符为e,第五个字符为a的员工名和工资
SELECT
	last_name,
	salary
FROM
	employees
WHERE
last_name LIKE '__n_l%';
#案例3:查询员工名中第二个字符为下划线的员工名
#查询方式1:使用转义字符 \
SELECT
	last_name
FROM
	employees
WHERE
	last_name LIKE '_\_%';

#(建议使用)查询方式2:使用任意字符,搭配  ESCAPE
SELECT
	last_name
FROM
	employees
WHERE
	last_name LIKE '_$_%' ESCAPE '$';

​ 2、between and

a:使用between and 可以提高语句简洁度 ; b:包含临界值

c:between and表示在 该区间,如果表示不在该区间,前面加NOT

SELECT
	*
FROM
	employees
WHERE
	employee_id BETWEEN 100
AND 120;

​ 3、in

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

b:特点提高语言简洁度;in列表中的值必须一致或兼容

c:不支持通配符的使用

#案例1:查询员工的工种编号是 IT_PROG、ID_VP中一个的员工名和工种编号
SELECT
	last_name,
	job_id
FROM
	employees
WHERE
	job_id IN ('IT_PROG', 'AD_VP');

​ 4、is null

a:注意,= 或 <>不可以判断null值

b:is null 或 is not null 可以判断null值

#案例1:查询没有奖金的员工名和奖金率
#方式1:使用 is null
SELECT
	last_name,
	commission_pct
FROM
	employees
WHERE
commission_pct IS NULL;
#方式2:使用安全等于  <=>
SELECT
	last_name,
	commission_pct
FROM
	employees
WHERE
	commission_pct <=> NULL;
#安全等于 <=>的其使用
#查询工资为12000的员工信息
SELECT
	*
FROM
	employees
WHERE
	salary <=> 12000;  #等同于  salary = 12000;

补充:<=>与IS NULL 的区别

IS NULL:仅仅可以判断 NULL 值,可读性高,建议使用;

<=>:既可以判断 NULL,又可以判断普通数值,可读性低;

=:不可以用来判断 NULL值

​ 5、ISNULL():

SELECT
commission_pct,ISNULL(commission_pct),CONCAT(last_name,first_name)
FROM
employees;
#运行结果:该字段为null的部分变为1(相当于true),其他为0(相当于false)

6、find_in_set:查询数据类型为set的字段是否包括某一项

 # 查询hobby这个数据类型为set的字段是否包含football	
 select * from student where find_in_set('football',hobby);

3.2.3 面试题

SELECT * FROM employees; 和 SELECT * FROM employees WHERE commission_pct LIKE ‘%%’ AND last_name LIKE ‘%%’;

试问结果是否一样?说明理由

答:不一样。如果判断的字段有null值,则不一样;如果没有,则一样

3.2.4 特别注意

① 条件查询 where 不允许使用“别名”

3.3、排序查询

3.3.1 语法

SELECT
	查询列表
FROM
	表
WHERE
	筛选条件
ORDER BY
	排序列表 【升序 ASC | 降序 DESC】

3.3.2 特点

① asc代表升序,desc代表的是降序,不写的情况下,默认升序;

② ORDER BY支持单个字段,多个字段排序,函数,表达式,别名

③ ORDER BY一般放在查询语句最后面,limit语句除外(在ORDER BY后面)

④ 执行顺序:③ ① ② ④

3.3.3 SQL语句实现

① 添加筛选条件

#案例1:查询部门编号>=90的员工信息,按入职的先后顺序排序
SELECT
	*
FROM
	employees
WHERE
	department_id >90
ORDER BY
hiredate ASC;

② 按表达式排序

#案例2:按年薪的高低显示员工的信息和年薪
SELECT
	*, salary * 12 * (1 + IFNULL(commission_pct, 0))
FROM
	employees
ORDER BY
	salary * 12 * (1 + IFNULL(commission_pct, 0)) DESC;

③ 把别名排序【案例2 升级版】

#案例3:按年薪的高低显示员工的信息和年薪
SELECT
	*, salary * 12 * (1 + IFNULL(commission_pct, 0)) AS 年薪
FROM
	employees
ORDER BY
	年薪 DESC;

④ 按函数排序

#案例4:按姓名的长度显示员工的姓名和工资
SELECT
	LENGTH(last_name) AS 字节长度,
	last_name,
	salary
FROM
	employees
ORDER BY
	LENGTH(last_name) DESC;

⑤ 按多个字段排序

#查询员工信息。要求先按工资排序,再按员工编号排序
SELECT
	*
FROM
	employees
ORDER BY
	salary DESC,
	employee_id ASC;
#查询结果:先按工资从高到低排序,如果出现工资一样的情况,再按编号从低到高排序

3.3.4 课后练习

# 查询年薪不在15万到16万员工的姓名、部门标号和年薪,按照年薪降序,工资升序
SELECT
	CONCAT(last_name, first_name) AS 姓名,
	department_id,
	salary * (1 + IFNULL(commission_pct, 0))*12 AS 年薪
FROM
	employees
WHERE
	#年薪 NOT BETWEEN 150000  #[Err] 1054 - Unknown column '年薪' in 'where clause'
salary * (1 + IFNULL(commission_pct, 0))*12 NOT BETWEEN 150000
AND 160000
ORDER BY
	年薪 DESC,
	LENGTH(last_name) ASC;
	
#此题目特别注意:条件部分 where 不允许使用别名
SELECT
	*,LENGTH(email)      #特别注意:追加的函数部分必须写在全部字段 * 后面
FROM
	employees
WHERE
	email LIKE '%e%'
ORDER BY
	LENGTH(email) DESC,department_id ASC;

3.4、常见函数

3.4.1 概念简介

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

② 好处:隐藏了实现细节;提高代码重用性

③ 调用:select 函数名(形参列表) 调用函数,其中逻辑语句执行完,将结果显示出来

④ 特点:函数名、函数功能

⑤ 分类:单行函数 例如:concat()、length()、ifnull()

​ 分组函数 功能:做统计适用,又称为统计函数、聚合函数、组函数

​ 特点:传进一组值,返回一个值

3.4.2 单行函数

3.4.2.1 字符函数

① length():获取参数值的字节数

SELECT LENGTH('Mary');  #4
SELECT LENGTH('玛丽哈哈哈');  #15
#注意:utf-8默认一个汉字三个字节,GBK默认一个汉字占用两个字节

② concat():拼接字符串

#拼接字段last_name与first_name,用下划线分割开
SELECT CONCAT(last_name,'_',first_name) FROM employees;

③ upper()与lower():用于将字符大小写转换

SELECT UPPER('asdfA');
SELECT LOWER('ASD');
#示例:将姓变大写,名变小写,然后拼接
SELECT CONCAT(UPPER(last_name),LOWER(first_name)) FROM employees;

④ substr、substring

#注意:索引从1开始
#截取从指定索引处后面所有的字符
SELECT SUBSTR('李莫愁爱上了陆展元' FROM 4);
SELECT SUBSTR('李莫愁爱上了陆展元',4);
#截取从索引处指定字符长度的字节
SELECT substr('李莫愁爱上了陆展元',7,3);
SELECT SUBSTR('李莫愁爱上了陆展元' FROM 7 FOR 3);

#案例:姓名中首字母大写,其它字母小写,用_拼接,显示出来
SELECT CONCAT(UPPER(SUBSTR(last_name,1,1)),LOWER(SUBSTR(last_name,2)))AS 姓名 FROM employees;

⑤ instr(str,substr):返回子串在主串中第一次出现的位置,如果找不到返回0

SELECT INSTR('杨不悔爱上了殷六侠','殷六侠');  #7

⑥ trim():

#去除前后的空格
SELECT trim('    张翠山   ') AS output;
#去除前后指定的字符
SELECT trim('a' from 'aaaaaaa张a翠山aaaa')AS output;
SELECT trim('aa' from 'aaaaaaa张a翠山aaaa')as output;  # 此时将'aa'看成一个字符

⑦ lpad():用指定的字符左填充至指定长度;如果超过指定长度,就会从右边开始截断

SELECT LPAD('殷素素',10,'*') AS output;   #	*******殷素素
#如果超过指定长度,就会从右边开始截断
SELECT LPAD('殷素素',2,'*') AS output;     #	殷素

⑧ rpad():用指定的字符右填充至指定长度;如果超过指定长度,与做填充一样

SELECT RPAD('殷素素',5,'*')AS output;     #	殷素素**

⑨ replace(str,from_str,to_str):如果from_str有多个,全部替换

SELECT REPLACE('周芷若张无忌爱上了周芷若','周芷若','赵敏') as output;
3.4.2.2 数学函数

① round():四舍五入

# 注意:不管正负,先取绝对值四舍五入,再加符号
# ROUND(X)
SELECT ROUND(3.14);   # 3
SELECT ROUND(-1.45);  # -1
#ROUND(X,D)  四舍五入,小数点后保留x位
SELECT ROUND(1.562,2);  # 1.56	

② ceil():向上取整,返回>=该参数的最小整数

SELECT CEIL(3.12);    # 4
SELECT CEIL(-3.12);   # -3

③ floor():向下取整,返回<=该参数的最大整数

SELECT FLOOR(-2.12);  # -3
SELECT FLOOR(2.12);   #  2

④ truncate(D,X):截断 保留小数点后X位

SELECT TRUNCATE(1.2999,1);   # 1.2

⑤ mod(D,X):D对X取余

#注意:被除数为正,结果为正;反之,为负
SELECT MOD(10,3);   # 1
SELECT MOD(-10,3);  # -1

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

SELECT
	RAND();
3.4.2.3 日期函数

① now:返回当前日期+时间

SELECT NOW() AS 时间;   # 2021-11-11 16:32:58

② curdate:返回当前系统的日期,不包含时间

SELECT CURDATE() AS 日期;  # 2021-11-11
SELECT CURRENT_DATE() AS 日期;   # 2021-11-11

③ curtime:返回当前系统的时间,不包含日期

SELECT CURTIME();	# 16:39:27
SELECT CURRENT_TIME();

④可以获取指定的部分:年(year)、月(month)、日(day)、时(hour)、分(minute)、秒(second)

# YEAR(date):参数为日期
SELECT YEAR(NOW());	# 2021
SELECT YEAR(hiredate) FROM employees;
# 获取月份:中文或英文
SELECT MONTH(NOW());    # 11
SELECT MONTHNAME(NOW());  # November

⑤ str_to_date(str,format):将字符串用指定格式解析

​ 日期相应的格式符

序号 格式符 功能
1 %Y 四位的年份
2 %y 2位的年份
3 %m 月份(01,02,···,12)
4 %c 月份(1,2,···,12)
5 %d 日(01,02,···,)
6 %H 小时(24小时制)
7 %h 小时(12小时制)
8 %i 分(00,01,···)
9 %s 秒(01,02,···)
SELECT STR_TO_DATE('12-7-15','%c-%d-%y');   # 2015-12-07
#查询入职日期为1992-4-3的员工信息
#按'%c-%d %Y'格式解析字符串'4-3 1992'
SELECT * FROM employees WHERE hiredate = STR_TO_DATE('4-3 1992','%c-%d %Y');

⑥ date_format(date,str):将日期转化为字符串

注意:此时date默认从左至右依次是年月日,数据输入有误,例如月份对应位置23,返回显示null\

SELECT DATE_FORMAT(NOW(),'%Y年%c月%d日');   # 2021年11月11日
#查询有奖金的员工名和入职日期(XX月/XX日 XX年)
SELECT last_name,DATE_FORMAT(hiredate,'%m月/%d日 %y年')AS 入职日期 FROM employees;

⑦ DATEDUFF():参数为两个日期

​ 功能:求两个日期之间的相差天数

SELECT
	DATEDIFF(NOW(), '1997-07-15') difference;
3.4.2.4 其他函数
#查看当前数据库的版本号
SELECT VERSION();
#查看当前的数据库
SELECT DATABASE();
#查看当前的用户
SELECT USER();
#加密
select password('字符');
3.4.2.5 流程控制函数

① IF (expr1,expr2,expr3)

#如果有奖金,返回Yes;反之,返回No
SELECT IF(commission_pct is NULL,'No','Yes')FROM employees;
SELECT IF(ISNULL(commission_pct),'No','Yes')FROM employees;   // 二者结果相同

② case函数的使用一:类似于switch case效果

回顾:java中switch case的用法:

Scanner scanner = new Scanner(System.in);
        int a = scanner.nextInt();         //给变量a赋值
        switch (a) {
            case 1:
                System.out.println("一层");
                break;                     //如果不写break;则不再进行判断,直接向下执行
            case 2:
                System.out.println("二层");
                break;
            default:                      //如果default位置不再最后,同样需要书写 break 语句
                System.out.println("输入有误");  // 没有以上case的内容,执行此句

MySQL中case的使用:

SELECT 
CASE 要判断的字段或表达式
WHEN 常量1 THEN 要显示的值或语句;   # 后面可能会把case当做单独语句,需要加分号
WHEN 可以多个
······
ELSE 默认情况
end 结尾
#案例:显示员工的工资,要求 部门号=30,显示工资为原来1.1倍;部门号=40,显示工资为原来1.2倍;部门号=50,显示工资为原来1.3倍;
#其他,显示原工资
SELECT
	last_name,
	salary,
	CASE department_id
WHEN 30 THEN           //when后是一个数值,不可写成 department_id = 30
	salary * 1.1      //注意:此位置是一个值,不可加 分号
WHEN 40 THEN
	salary * 1.2
WHEN 50 THEN
	salary * 1.3
ELSE
	salary
END AS 新工资
FROM
	employees;

② case函数的使用二:类似于 多重 if

/*
CASE
WHEN 条件1 then 要显示的值或语句1
WHEN 条件2 then 要显示的值或语句2
······
ELSE 要显示的值n或语句n
END
*/
/*案例:查询员工的工资情况
如果工资>20000,显示A;如果工资>15000,显示B;如果工资>10000,显示C;否则,D
*/
SELECT
	last_name,
	salary,
	CASE
WHEN salary > 20000 THEN
	'A'
WHEN salary > 15000 THEN
	'B'
WHEN salary > 10000 THEN
	'C'
ELSE
	'C'
END AS 工资等级
FROM
	employees;

3.4.3 分组函数

3.4.3.1 功能

用做统计适用,又称为聚合函数或统计函数、组函数

分类:sum 求和、avg 平均值、max 最大值、min 最小值、count 计算个数

3.4.3.2 简单使用
SELECT
	SUM(salary),
	AVG(salary),
	MAX(salary),
	MIN(salary),
	COUNT(salary)
FROM
	employees;
3.4.3.3 特别注意

① SUM和AVG一般适合数值型的运算;

② MAX、MIN、COUNT可以处理任何类型

③ 忽略NULl值:SUM、AVG、MAX、MIN、COUNT(计算非空数值的个数)

​ 总结,所有的分组函数都忽略 NULl值

④ 可以和distinct搭配使用

# 都可以搭配 distinct
SELECT
	SUM(DISTINCT salary),
	SUM(salary)
FROM
	employees;

⑤ 和分组函数一同查询的字段要求是group by后的字段

3.4.3.4 COUNT函数

① 使用方式一:用于统计数值不为null的个数,即值为null的不计入总数

SELECT
	COUNT(salary)
FROM
	employees;

② 使用方式二:假如表中有n个字段,只要其中有一个字段的值不为null,就计入总数

SELECT
	COUNT(*)
FROM
	employees;

③ 使用方式三:

# count中可以是一个常量值,相当于在表中加了一列,通过统计该常量值的个数,统计表中信息的条数
SELECT
	COUNT(1)   # 内容随机
FROM
	employees;

④ 三种方式效率比较:一般使用**【方式二】**统计行数

​ MyISAM存储引擎,count()最高;InnoDB存储引擎,COUNT(1)和COUNT()>count(字段)

⑤ 和分组函数一同查询的字段要求:group by后的字段

3.5、分组查询 1

3.5.1 GROUP BY语法

SELECT
	column(列),group_function(column)   #分组查询搭配该分组函数,colum列必须要出现在group_by_expression这个表达式后面
FROM
	表名
WHERE
	条件表达式、逻辑表达式、模糊查询
GROUP BY
	group_by_expression
ORDER BY           #表示按该字段排序
	字段  

注意:查询列表必须特殊,要求是分组函数和GROUP BY后出现的字段【即:和分组函数】

3.5.2 SQL语句的实现

3.5.2.1 简单的分组查询
#案例1:查询每个工种的最高工资
SELECT
	MAX(salary),
	job_id
FROM
	employees
GROUP BY
	job_id;
#案例2:查询每个位置上的部门个数
SELECT
	COUNT(*),
	location_id
FROM
	departments
GROUP BY
	location_id;
3.5.2.2 分组查询–添加简单筛选
#案例1:查询邮箱中包含字符a的,每个部门的平均工资
SELECT
	AVG(salary) AS 部门平均工资,
	department_id
FROM
	employees
WHERE
	email LIKE '%a%'
GROUP BY
	department_id;
#案例2:查询有奖金的每个领导手下员工的最高工资
SELECT
	MAX(salary),
	manager_id
FROM
	employees
WHERE
	commission_pct IS NOT NULL
GROUP BY
	manager_id;
3.5.2.3 分组查询–添加复杂筛选–HAVING

HAVING的使用:进行分组后的筛选,HAVING只可以跟一个筛选条件,不可以跟多个HAVING

#案例1:查询哪个部门的员工个数>2
#步骤1:查询每个部门的员工个数
#步骤2:根据步骤1的结果,查询哪个部门人数>2
SELECT
	COUNT(*) AS 员工个数,
	department_id AS 部门编号
FROM
	employees
GROUP BY
	department_id
HAVING
	COUNT(*) > 2;
/*
案例2:查询每个工种有奖金的员工的最高工资>12000的工种编号和最高工资
步骤1:先查询每个工种有奖金的最高工资
步骤2:在筛选最高工资大于12000的工种
*/
SELECT
	MAX(salary),
	job_id
FROM
	employees
WHERE
	commission_pct IS NOT NULL
GROUP BY
	job_id
HAVING
	MAX(salary) > 12000;
#案例3:查询领导编号>102的每个领导手下的最低工资>5000的领导是哪个,以及最低工资
#步骤1:先查询每个领导手下的最低工资
#步骤2:再筛选最低工资大于5000的领导
SELECT
	MIN(salary),
	manager_id
FROM
	employees
WHERE
	manager_id > 102
GROUP BY
	manager_id
HAVING
	MIN(salary) > 5000;
3.5.2.4 总结

① 分组查询中的筛选条件分为两类

类型 数据源 位置 关键字
分组前筛选 原始表 group by子句的前面 where
分组后筛选 分组后的结果集 group by子句的后面 having

② 分组函数做条件必定放在having子句中

③ 优先使用分组前筛选

④ where筛选条件后面是不支持别名的;而group by、order by和 having 是支持别名的

⑤ SELECT 中可以包含多个分组函数;除分组函数,其他在SELECT中出现的表达式,GROUP UP也应该出现,否则报1055错误

⑥ GROUP BY支持多个分组,表达式或函数(用的较少)

⑦ 最后也支持分组后的排序ORDER BY

3.5.2.5 按表达式或函数分组

① 添加筛选条件

#案例1:按员工姓名的长度分组,查询每一组的员工个数,筛选员工个数>5的有哪些
#步骤1:查询每个长度的员工个数
#步骤2:添加筛选条件
SELECT
	COUNT(*),
	LENGTH(last_name)
FROM
	employees
GROUP BY
	LENGTH(last_name)
HAVING
	COUNT(*) > 5
ORDER BY
	COUNT(*);

② 多个字段分组,同时SELECT支持存在多个分组函数

#按多个字段分组
#案例:查询每个部门每个工种的员工的平均工资
SELECT
COUNT(salary),
	avg(salary),
	SUM(salary),
	department_id,
	job_id
FROM
	employees
GROUP BY
	department_id,
	job_id;

③ 添加排序

SELECT
COUNT(salary),
	avg(salary) AS 平均工资,
	SUM(salary),
	department_id,
	job_id
FROM
	employees
GROUP BY
	department_id,
	job_id
ORDER BY
平均工资 ASC;

3.6、连接查询

3.6.1 简介

① 连接查询,又称多表查询,当查询字段来自多个表时,就会用到多表查询

② 笛卡尔积的错误情况:

#语法
SELECT 
	name,boyName
FROM
	beauty,boys;
#假设:beauty表由12行,boys表有4行,上述SQL语句输出48行
#原因:上述语句相当于拿着第一张表的每条记录挨个匹配第二张表,因为此时没有筛选条件也没有连接条件,就会全部连接成功

③ 笛卡尔积的避免方式:添加有效的连接

④ [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rCoEFpTo-1682066831850)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20211112172449136.png)]

3.6.2 SQL92标准

3.6.2.1 等值连接-添加筛选AND

① 表名取别名:与字段取别名一致,表名 AS 别名,AS 可省略

​ 好处:提高语句的简洁度;区分多个重名的字段;如果为表取了别名,就不可以使用原来表明限定

② 分别找出两个表中表示相同含义的字段,在where中建立连接

#案例1:查询女神和对应男神名
SELECT
	NAME,
	boyName
FROM
	beauty,
	boys
WHERE
	beauty.boyfriend_id = boys.id;
#案例2:查询员工名和对应的部门名
SELECT
	last_name,
	department_name
FROM
	employees,
	departments
WHERE
	employees.department_id = departments.department_id;
#查询员工名、工种名、工种号
SELECT
	last_name,
	employees.job_id,                #由于employees与jobs中都有job_id,所以需要一个表名限定一下,两个表名都可以;
	job_title
FROM
	employees,
	jobs
WHERE
	employees.job_id = jobs.job_id;

③ 两个表的位置是否可以调换

​ 可以调换,原因:在执行过程中,用一张表去和另外一张表匹配,与表的顺序无关

④ 是否可以加筛选:

​ AND连接另外一个筛选条件, AND 后面只可以跟一个筛选条件,但是可以跟多个AND

#案例:查询有奖金的员工名和部门名
SELECT
	commission_pct,
	last_name,
	department_name
FROM
	employees,
	departments
WHERE
	employees.department_id = departments.department_id
AND employees.commission_pct IS NOT NULL;


#查询城市名中第二个字符为o的部门名和城市名
SELECT
	department_name,
	CONCAT(
		country_id,
		city,
		street_address
	)
FROM
	locations,
	departments
WHERE
	departments.location_id = locations.location_id
AND city LIKE '_o%'                                   # AND 后面只可以跟一个筛选条件,但是可以跟多个AND
AND department_name != 'IT';

⑤ 是否可以加分组

#案例1:查询每个城市的部门个数
SELECT
	COUNT(*),
	city
FROM
	locations,departments
WHERE
	departments.location_id = locations.location_id
GROUP BY
	city;
#案例2:查询有奖金的每个部门的部门名和部门领导的编号和该部门的最工资
SELECT
department_name,employees.department_id,MIN(salary)
FROM
employees,departments
WHERE
employees.department_id = departments.department_id
AND
commission_pct is not NULL
GROUP BY
department_name,department_id;

⑥ 是否可以加入排序

#案例:查询每个工种的工种名和员工的个数,并且按员工的个数降序
SELECT
	COUNT(*),
	job_title
FROM
	employees,
	jobs
WHERE
	employees.job_id = jobs.job_id
GROUP BY
	job_title
ORDER BY
	COUNT(*);

⑦ 是否可以实现三表连接?

#案例:查询员工名、部门名和所在的城市
SELECT
	last_name,
	department_name,
	city
FROM
	employees,
	departments,
	locations
WHERE
	employees.department_id = departments.department_id
AND departments.location_id = locations.location_id;
3.6.2.2 非等值连接

​ 可以搭配前面介绍的所有子句,比如:分组、筛选、排序

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iQXIgVox-1682066831852)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20211112212054081.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7Ylde9Nh-1682066831853)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20211112212147588.png)]

#查询员工的工资和工资级别
SELECT
	salary,
	grade_level
FROM
	employees,
	job_grades
WHERE
	salary BETWEEN lowest_sal
AND highest_sal;
3.6.2.3 自连接

概念:把自己当作多张表使用,这时就要使用 别名

例如:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LSGhwGX4-1682066831853)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20211112212857092.png)][外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OI5no5OE-1682066831854)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20211112212907552.png)]

#案例:查询员工名和上级的名字
#步骤1:先查询到领导的manager_id;
#步骤2:通过manager_id在同张表中查询名字
SELECT
	e.employee_id,
	e.last_name,
	m.employee_id,
	m.last_name
FROM
	employees e,
	employees m
WHERE
	e.manager_id = m.employee_id;
3.6.2.4 特别注意

(1)等值连接相关注意事项

① 如果为表取了别名,就不可以使用原来表明限定

② n表连接,至少需要n-1个连接条件,用 AND

③ 多表的顺序没有要求

④ 一般需要给表取别名

⑤ 可以搭配前面介绍的所有子句,比如:分组、筛选、排序

⑤ 添加筛选条件的关键字:WHERE、HAVING、AND的区别

​ 答:WHERE语句在GROUP BY语句之前;SQL会在分组之前计算WHERE语句。
​ HAVING语句在GROUP BY语句之后;SQL会在分组之后计算HAVING语句;HAVING只允许存在1个,且判断条件只允许1个。

​ AND常用在WHERE后面,添加筛选条件,只允许存在一个条件,但是可以跟多个AND

3.6.3 SQL99标准

3.6.3.1 简介

① 语法:

SELECT
	查询列表
FROM
	表1 AS 别名 【连接类型】
JOIN 表2 AS 别名 ON 连接条件
【WHERE 筛选条件】               #【】表示可选
【GROUP BY 分组】
【HAVING 筛选条件】
【ORDER BY 排序列表】

② 分类

内连接(☆☆☆):inner

外连接:左外连接(☆☆☆):left【outer】

​ 右外连接(☆☆☆):right【outer】

​ 全外连接:full【outer】

交叉连接:cross

3.6.3.2 内连接-等值连接

① 语法:

SELECT
	查询列表
FROM
	表1 别名
INNER JOIN 表2 别名 ON 连接条件;

② 代码举例:

#案例1:查询员工名、部门名
SELECT
	last_name,
	department_name
FROM
	employees
INNER JOIN departments ON departments.department_id = employees.department_id;
#案例2:查询名字中包含e的员工名和工种名     【增加筛选条件】
SELECT
	last_name,
	job_title
FROM
	employees
INNER JOIN jobs ON employees.job_id = jobs.job_id
WHERE                                                   #增加筛选条件
	last_name LIKE '%e%';
#案例3:查询部门个数超过3的城市名和部门个数    【增加分组、增加分组后的筛选】
SELECT
	COUNT(*),
	city
FROM
	departments
INNER JOIN locations ON departments.location_id = locations.location_id
GROUP BY                                                 #增加分组
	city
HAVING                                                   #增加分组后的筛选
	count(*) > 3;
#案例4:查询部门的员工个数>3的部门名和员工个数,并按个数排序  【增加排序】
SELECT
	department_name,
	COUNT(*)
FROM
	employees e
INNER JOIN departments d ON d.department_id = e.department_id
GROUP BY
	department_name
HAVING
	COUNT(*)
ORDER BY                                                  #增加排序
	COUNT(*);
#查询员工名、部门名、工种名,并按部门名降序       【涉及多表连接】
SELECT
	last_name,
	department_name,
	job_title
FROM
	employees e
INNER JOIN departments d ON d.department_id = e.department_id
INNER JOIN jobs j ON j.job_id = e.job_id
ORDER BY
	department_name DESC;


③ 特点:

​ 添加筛选、分组、排序;

​ INNER可以省略;

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

3.6.3.3 内连接-非等值连接
#查询员工的工资级别
SELECT
	last_name,
	grade_level
FROM
	employees e
INNER JOIN job_grades j ON e.salary BETWEEN j.lowest_sal
AND j.highest_sal
ORDER BY
	grade_level DESC;

3.6.3.4 内连接-自连接
#案例:查询员工的名字和上级的名字
SELECT
	e.last_name AS A,
	m.last_name AS A的上级
FROM
	employees e
INNER JOIN employees m ON e.manager_id = m.employee_id;
3.6.3.4 外连接

① 用于查询一个表中有,另外一个表中的没有的记录

② 外连接的查询结果为主表中的所有记录;

​ 如果从表中有与其匹配的,显示匹配结果的值;如果没有,显示null

​ 外连接结果 = 内连接结果 + 主表有而从表没有的结果

③ 左外连接,left join 左边的是主表;右外连接,right join右边的是主表

④ 左外、右外交换表的顺序可以达到相同的效果

#案例:查询男朋友不在男神表的女生名   【左外连接】
SELECT
	*
FROM
	beauty g
LEFT JOIN boys b ON g.boyfriend_id = b.id
WHERE
	b.id IS NULL;       # 此时用b.id原因,id是主键,不会为nll,其他字段可能为null,使用其他字段筛选结果可能错误
#查询没有员工的部门
SELECT
	*
FROM
	departments d
LEFT JOIN employees e ON e.department_id = d.department_id
WHERE
employee_id <=>NULL;             #注意:使用主键,主键不为null

⑤ 全外连接

实现效果:全外连接 = 内连接结果 + 表1有但2没有 + 表2有但表1没有

# 当前MySQL不支持
SELECT
	*
FROM
	beauty b
FULL OUTER JOIN boys bo ON b.boyfriend_id = bo.id;
3.6.3.5 交叉连接

① 没有顺序关系

② 使用99语法标准实现笛卡尔乘积

3.7、子查询

3.7.1 相关介绍

① 含义:出现在其它语句中的select语句,称为子查询或内查询;外部的查询语句,称为主查询或外查询

② 分类:

​ 按子类查询出出现的位置:

​ select后面:仅支持标量子查询

​ from后面:支持表查询

​ ☆☆☆ where后面:标量子查询 ☆、列子查询 ☆、行子查询

​ having后面:标量子查询、列子查询、行子查询

​ exists后面(相关子查询):表子查询

​ 按结果集的行列数不同:标量子查询(结果集只有一行一列)、列子查询(结果集一列多行)、行子查询(一行多列)、表子查询

3.7.2 where或having

3.7.2.1 标量子查询 ☆☆☆
#步骤1:查询Abel的工资
#步骤2:查询员工信息,salary大于步骤1的结果
SELECT
	last_name,
	salary
FROM
	employees
WHERE
	(
		SELECT
			salary
		FROM
			employees
		WHERE
			last_name = 'Abel'
	) < salary;
#案例2:返回job_id与141号员工相同,salary比143号员工多的员工的姓名、job_id、工资
SELECT
	last_name,
	job_id,
	salary
FROM
	employees
WHERE
	job_id = (
		SELECT
			job_id
		FROM
			employees
		WHERE
			employee_id = 141
	)
AND salary > (                       #可以支持多个子查询,用AND连接
	SELECT
		salary
	FROM
		employees
	WHERE
		employee_id = 143
);
#案例3:返回公司工资最少的员工的last_name,job_id和salary
SELECT
	last_name,
	job_id,
	salary
FROM
	employees
WHERE
	salary = (
		SELECT
			MIN(salary)
		FROM
			employees
	);
#案例4:查询最低工资大于"50号部门的最低工资"的部门id和其最低工资
SELECT
	MIN(salary),
	department_id
FROM
	employees
GROUP BY
	department_id
HAVING
	MIN(salary) > (
		SELECT
			MIN(salary)
		FROM
			employees
		WHERE
			department_id = 50
		GROUP BY
			department_id
	);
3.7.2.2 列子查询 ☆☆☆

​ (一列多行),又称为多行子查询

​ 列子查询,一般搭配着多行操作符使用(in、any/some、all)

操作符 含义 备注
IN、NOT IN 等于列表中任意一个 与a=ANY()含义相同
ANY 、 SOME 和子查询返回的某个值比较 例如:a>ANY(10,23,25),意思大于其中一个即可,可用min代替
ALL 和子查询返回的所有值比较 例如:a>ALL(a0,23,25),意思大于所有制,可用max代替
#案例1:返回location_id是1400或1700的部门中所有员工的姓名
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
SELECT
	employee_id,
	last_name,
	job_id,
	salary
FROM
	employees
WHERE
	job_id <> 'IT_PROG'
AND salary < ANY (              
	SELECT
		salary
	FROM
		employees
	WHERE
		job_id = 'IT_PROG'
);
/*
AND可替换为:
AND salary < (
	SELECT
		MAX(salary)
	FROM
		employees
	WHERE
		job_id = 'IT_PROG'
);*/
3.7.2.3 行子查询

​ 结果集是一行多列或者多行多列

​ 行子查询局限性比较大:将多个字段看作一行(即看作一个虚拟的字段),两个字段作为条件有一定的规律

#案例3:查询员工编号最小并且工资最高的员工信息
#方式1:
SELECT
	*
FROM
	employees
WHERE
	employee_id = (
		SELECT
			MIN(employee_id)
		FROM
			employees
	)
AND salary = (
	SELECT
		MAX(salary)
	FROM
		employees
);

#方式2:行子查询
SELECT
	*
FROM
	employees
WHERE
	(employee_id, salary)=(
		SELECT
			MIN(employee_id),
			MAX(salary)
		FROM
			employees
	);

4、特点:①子查询放在小括号内;②子查询一般放在条件的右侧;

​ ③标量子查询,一般搭配着单行操作符使用(> 、<、 >=、 <= 、<>)

​ ④列子查询,一般搭配着多行操作符使用(in、any/some、all)

​ ⑤子查询优于主查询的执行

3.7.3 select --了解

① 仅仅支持标量子查询

#案例1:查询每个部门的员工个数
SELECT
	departments.*, (
		SELECT
			COUNT(*)
		FROM
			employees
		WHERE
			departments.department_id = employees.department_id
	)
FROM
	departments;

② 执行过程

首先我们假设有表A,B
现在我们想知道A中的所有属性a,于是我们写出

select A.a from A;

这个时候,数据库便会去遍历A表中的每一行,并将其作为结果输出,假设A表中有n行,那么最后的结果也就是n行

在此基础上,如果我们不光想知道的所有的A.a,还有在B表中,有多少行B.a 与 A.a对应的A.b相等,于是我们写出

select A.a,( select count(*) from B where B.a = A.b ) from A;

数据库仍然会去遍历A表中的每一行,对于每一行,子查询中的A.b也会获得一个值,用于完成子查询,子查询的结果被附在结果集上,被输出出来
也就是说,select后面的子查询不会影响结果的行数,只是可以把from后方,表每一行的属性借来一用

对于exists也是如此,只不过它可以改变结果的数量

3.7.4 from

将SELECT的结果集作为一个表来使用

注意:from后面的子查询必须使用别名

#查询每个部门的平均工资的工资等级
SELECT
	j.grade_level,
	av.department_id
FROM
	job_grades AS j
INNER JOIN (
	SELECT
		avg(salary) 平均工资,
		department_id
	FROM
		employees
	GROUP BY
		department_id
) AS av ON av.平均工资 BETWEEN j.lowest_sal
AND j.highest_sal;


3.7.5 exists

① 成为相关子查询

② 语法: EXISTS (完整的查询语句) 结果: 1或0

③ 只关心有无查询结果

#案例1:查询有员工的部门名
SELECT
	department_name
FROM
	departments
WHERE
	EXISTS (
		SELECT
			*
		FROM
			employees
		WHERE
			employees.department_id = departments.department_id
	);
#执行次序:每解析departments表中的每一行,都会进行一次判断,即用当前的departments.department_id值去遍历employees.department_id ,满足几次,就会有几行

3.7.6 课后练习

#:案例1:查询平均工资最低的部门信息
SELECT
	de.*
FROM
	departments de
INNER JOIN (
	SELECT
		department_id,
		AVG(salary) avs
	FROM
		employees
	GROUP BY
		department_id
) av ON de.department_id = av.department_id
ORDER BY
	avs ASC
LIMIT 0,1;
#案例2:查询平均工资最高的job信息
#步骤1:查询各个job的平均工资
#步骤2:降序,输出第一行
SELECT
	j.*, jv.av
FROM
	jobs j
INNER JOIN (
	SELECT
		AVG(salary)AS av,
		job_id
	FROM
		employees
	GROUP BY
		job_id
) AS jv ON jv.job_id = j.job_id
ORDER BY
	av DESC
LIMIT 1;
#案例3:查询公司中所有manager的信息
#步骤1:建立自连接,m表为主表,去匹配e表;
#步骤2:如果e表中没有与m表匹配的数据,显示为null,此时表示该管理者没有员工。用e的主键筛选
SELECT DISTINCT
	m.last_name
FROM
	employees m
LEFT JOIN employees e ON m.employee_id = e.manager_id
WHERE
	e.employee_id IS NOT NULL;


#案例4:查询各个部门中 最高工资中最低的那个部门 最低工资是多少
SELECT
	MIN(salary)
FROM
	employees
WHERE
	department_id = (
		SELECT
			department_id
		FROM
			employees
		GROUP BY
			department_id
		ORDER BY
			MAX(salary)
		LIMIT 1
	);


3.8、分页查询

3.8.1 应用场景

​ 当要显示的数据,一页显示不全,需要分页提交sql请求

3.8.2 语法

SELECT	⑦
	查询列表
FROM	①
	表 【join type
JOIN	② 表2 ON 连接条件	③
WHERE	④
	筛选条件
GROUP BY	⑤
	分组字段
HAVING	⑥
	分组后的筛选
ORDER BY	⑧
	排序字段】
LIMIT 【OFFSET】,	⑨
 size;
# offset为起始索引(起始索引从0开始),size代表显示的条目个数

3.8.3 案例

#案例1:查询员工前五条信息
SELECT
	*
FROM
	employees
LIMIT 0,                   #如果索引从第一条开始,支持省略
 5;
#案例2:有奖金的员工信息,并且工资较高的前10名显示出来
SELECT
	*
FROM
	employees
WHERE
	commission_pct IS NOT NULL
ORDER BY
	salary DESC
LIMIT 10;

3.8.4 特点

① limit子句放在查询语句的最后,执行顺序也是最后

② 公式

​ 要现实的页数 page ,每页的条目数size

​ SELECT 查询列表

​ FROM 表

​ LIMIT (page-1)*size,size

3.9、联合查询

3.9.1 简介

① union:联合,合并:将多条查询语句的结果合并为一个结果

② 引入案例:

#查询部门编号>90或邮箱包含a的员工信息
#方式1:
SELECT * FROM employees WHERE department_id > 90 OR email LIKE '%a%';

#方式2:
SELECT * FROM employees WHERE department_id > 90
UNION
SELECT * FROM employees WHERE email LIKE '%a%';

③ 可以多个union

④ 使用场景:当查询结果来自多个表,且多个表之间没有明显的连接关系,但查询的信息一致

3.9.2 特别注意 ☆☆☆

① 要求多条查询语句查询结果的列数一致

② 要求多条查询语句的每一列的类型和顺序最好一致

③ union关键字默认去重,如果使用union all可以包含重复项

3.10 总结

① 语法及执行顺序

select 查询列表	#	7
from 表1 别名          #	1
inner|left|right|full|cross join 表2 别名  #	2	
on 连接条件  # cross不需要    #	3
where 筛选条件	#	4
group by 分组字段	#	5
having 筛选条件	#	6
order by 排序列表	#8
limit 起始索引,条目数	#9

4、DML语言的学习

4.1、插入语句

4.1.1 经典插入一

语法

INSERT INTO 表名(列名,...)
VALUES(值1,,...)

① 插入的值的类型要与列的类型一致或兼容

INSERT INTO beauty(id,name,sex,borndate,phone,photo,boyfriend_id)
VALUES(13,'唐艺昕','女','1997-07-12','18923456789',NULL,2);

② 不可以为null的列必须插入值。可以为null的列如何插入值?

#方式1:同上①
#方式2:列名和数据都不填写,默认为Null

③ 列的顺序可以调换

④ 列数和值的个数必须一致

⑤ 可以省略列名,默认所有列,顺序与表中一致

⑥ 插入数据类型有集合:该字段内容 ’sing,dance’ 此种格式写,即逗号将其隔开,引号引住

INSERT INTO interest VALUES(1,'Tom','sing,dance',16800.00,'B','表现不错,进步好快');

4.1.2 经典插入二

语法:

insert into 表名
set 列名=值,...

4.1.3 特别注意 ☆☆☆

方式一:支持插入多行

INSERT INTO beauty
VALUES(23,'Mary','女','1990-2-3','1234343',null,2),
(24,'Mary1','女','1990-2-3','1234343',null,2),
(25,'Mary2','女','1990-2-3','1234343',null,2)

方式一:支持子查询(可以是一个表)

相当于子查询生成一个结果集,然后对用列表插入

INSERT INTO beauty
SELECT 26,'宋茜','女','1990-2-3','1234343',null,2;

4.2、修改语句

4.2.1 修改单表记录

4.2.1.1 语法
update 表名                 #执行顺序 ①
set 列=新值,列=新值,...      #如果不加筛选条件,相当于更改了所有列     执行顺序③
where 筛选条件              #执行顺序 ②
#除数字外需要加单引号 
4.2.1.2 课后练习
#案例1:修改表中姓唐的女生的电话为18003418765
UPDATE beauty
SET phone = '18003418765'
WHERE
	NAME LIKE '唐%';

4.2.2 修改多表记录

4.2.2.1 语法
#SQL92语法
update 表1 别名,表2 别名
set 列=值,...
where 连接条件
and 筛选条件;
#SQL99语法
update 表1 别名
inner |left|right join 表2 别名
on 连接条件
set 列=值,...
where 连接条件
and 筛选条件;

4.2.2.2 课后练习
#修改张无忌女朋友的手机号为114
UPDATE boys bo
INNER JOIN beauty b ON bo.id = b.boyfriend_id
SET b.phone = '114'
WHERE
	bo.boyName = '张无忌';

#修改没有男朋友的女神的男朋友编号都为5号张飞
UPDATE beauty b
LEFT JOIN boys bo ON b.boyfriend_id = bo.id
SET b.boyfriend_id = 5
WHERE
	bo.id IS NULL;

4.3、删除语句

4.3.1 删除方式1

4.3.1.1 语法
#单表删除 ☆☆☆
delete from 表名 where 筛选条件【limit 1】
#limit在delete语句中,使用limit参数的时候不能传两个参数,只能传一个参数,即limit 1;这样就表示只能删除一条数据了。
#注意要区分与select中limit区别
#多表删除 ☆
delete 要删除的表名
from 表1 别名
inner|left|right join 表2 别名
on 连接条件
where 筛选条件
4.3.1.2 单表删除
#删除手机号9结尾的女神信息
DELETE FROM beauty WHERE phone LIKE '%9';
4.3.1.3 多表删除
#删除张无忌的女朋友的信息
DELETE b
FROM
	boys bo
INNER JOIN beauty b ON bo.id = b.boyfriend_id
WHERE
	bo.boyname = '张无忌';
#删除黄晓明的信息以及女朋友的信息
DELETE b,
 bo
FROM
	boys bo
INNER JOIN beauty b ON b.boyfriend_id = bo.id
WHERE
	bo.boyName = '黄晓明';

4.3.2 删除方式2

4.3.2.1 语法
truncate table 表名;

4.3.3 特别注意 ☆☆☆

① delete可以加where条件,truncate不可以加

② truncate删除,效率高

③ 假如要删除表中有自增长列,如果用delete删除后,在插入数据,自增长列的值从断点开始;而truncate删除后,再插入数据,自增长列的值从1开始

#解析:
#delete删除表:假设boys表中的id是自增长列,到5
delete from boys;
#此时表中信息全部被删除,此时插入信息
insert into boys(name,usercp)
values(,)(,)
#此时表中的id自动生成,从6开始
#truncate删除表后,插入数据,id从1开始

④ truncate删除没有返回值,delete删除有返回值【即控制台是否会显示几行受影响】

⑤ truncate删除不可以回滚,delete删除可以回滚

5、DDL语言的学习

5.1、库的管理

5.1.1 库的创建

#语法:
create database [if not exists]库名   含义:如果数据库不存在则创建,加上后不会报错"数据库已存在"
#举例:创建数据库Books
create database Books;

5.1.2 库的修改

#更改库的字符集
ALTER DATABASE BOOKS CHARACTER SET GBK;

5.1.3 库的删除

#语法:
drop database [if exists] 库名;   含义:如果存在,删除
#举例:删除数据库Books
drop database Books;

5.1.4 更改库名

​ 只可以从安装目录中data修改。修改步骤:1、关闭mysql服务;2、修改;3、开启服务

5.2、表的管理

5.2.1 表的创建

#语法:
create table 表名(
    列名 列的类型 【(长度) 约束】,
    列名 列的类型 【(长度) 约束】,
    ......
    列名 列的类型 【(长度) 约束】,
)
#创建book表
CREATE TABLE book (
	id INT,
	#书的编号
	bname VARCHAR (20),
	#书名
	price DOUBLE,
	#价格
	authorId INT,
	#作者编号
	publishDate DATETIME 
     #出版日期  
);

5.2.2 表的修改

① 修改列名

Alter TABLE book CHANGE 【COLUMN】 publishdate(原列名) putDate(新列名) DATETIME;

② 修改列的类型或约束

ALTER TABLE book MODIFY COLUMN putDate TIMESTAMP(新类型);

③ 添加新列

ALTER TABLE author ADD COLUMN annual(新列名) DOUBLE 【first、after 列名1】;
# first表示添加该列在第一个位置,after 列名1:表示添加列位于列名1下个位置

④ 删除列

ALTER TABLE author DROP COLUMN annual;

⑤ 修改表名

ALTER TABLE author rename to book_author;

⑥ 总结语法

ALTER TABLE 表名 change|add|drop|rename to|modify column 列名 【列类型 约束】;

5.2.3 表的删除

drop table 【if exists】 表名;    # if exists 仅仅在库和表创建和删除时有,此后不会报错

5.2.4 表的复制

① 仅仅复制表的结构

create table copy like author;   # copy 复制得到的表名   author  数据库中存在的表名

② 复制表的数据+结构

create table copy2
select *from author;   # 相当于将select语句生成的结果集,放入

③ 复制部分数据

create table copy3
select 字段1,字段2,...
from 表名             #如果是其他数据库的表,库名.表名
where 筛选条件;

④ 仅仅复制某些字段

create table copy4
select 字段1,字段2,...
from 表名
where 1=2;     #此时筛选条件随机,不成立即可

5.3、数据类型

5.3.1 常见数据类型

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZmK4gzpA-1682066831856)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20211114194645564.png)]

5.3.2 整型

① 分类:

整数类型 字节 范围
Tinyint 1 有符号:-128-127、无符号:0-255
Smallint 2 有符号:-32768-32767、无符号:0-65535
Mediumint 3 不用记
int、integer 4 不用记
Bigint 8 不用记

② 如何设置无符号和有符号?

​ 答:如果不设置有符号还是无符号,默认有符号;如果想设置无符号,需添加unsigned

drop table if exists tab_int;     #如果有此表,删除;避免重新建表出现错误
create table tsb_int (t1 INT,t2 INT UNSIGNED);  # t1默认有符号,t2无符号,不可存入负数

③ 如果插入的数据超出了整形的范围,会报out of range异常,并且插入临界值

④ 如果不插入长度,会有默认的长度。

​ 长度代表了显示的最大宽度,并不代表该类型可表示的范围。

​ 如果不够用0填充,但必须搭配zerofill使用

drop table if exists tab_int;     
create table tsb_int (
    t1 INT(7) zerofill,   #使用关键字zerofill后,同时代表无符号,不可插入负数
    t2 INT UNSIGNED
);

5.3.3 小数

① 分类:

浮点数类型 字节 范围
float(M,D) 4 忽略
double(M,D) 8 忽略
定点数类型 字节 范围
DEC(M,D)=decimal(M,D) M+2 精度比double高

② M和D分别表示:D表示小数点后保留几位,M表示整数部位+小数部位;如果超出范围,则插入临界值

③ M和D都可以省略。如果是定点型,则M默认为10,D默认为0;如果是浮点型,会根据插入数的精度调整

④ 定点型的精度高。如果要求插入数值的精度较高,如货币运算考虑使用

⑤ 原则:选择类型越简单越好,能保存数值的类型越小越好

5.3.4 字符

① 较短的文本

类型 M的意思 特点 空间耗费 效率 应用
char 最大的字符数,可以省略,默认为1 固定的字符长度 耗费 数据只有一个字符,例如:性别
varchar 最大的字符数,不可以省略 可变的字符长度 节省

② 枚举类型:只能选择其中一个插入

create table if exists tab_char(      #不区分大小写         
    c1 ENUM('a','b','c')
);      #	c1字段插入的数据,只能是枚举中的数值(只能选择其中一个插入);如果不是,插入失败

③ 集合类型:可选择多个插入

create table if exists tab_char(		#不区分大小写
    c1 SET('a','b','c')
);  
insert into tab_char
values('a'),('a,b');        

5.3.5 日期

日期和时间类型 字节 保存类型
date 4 只保存日期
datetime 8 范围1000年至9999间的日期+时间,不受时区的影响
timestamp 4 时间戳,范围1970年至2038年间的日期+时间,受时区的影响
time 3 只保存时间
year 1 只保存年份

5.4 常见约束

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

六大约束 含义 使用场景
NOT NULL 非空,用于保证该字段的值不能为空 姓名,学号等
DEFAULT 默认,用于保证该字段有默认值 性别
PRIMARY KEY 主键,保证该字段值的唯一性,并且非空 学号、员工编号
UNIQUE 唯一,保证该字段值的唯一性,可以为空 座位号
CHECK 检查约束【mysql不支持】 年龄、性别【只能是规定的内容】
FOREIGN KEY 外键,用于限制两个表的关系,用于保证该字段的值来自主表的关联列的值 学生表的专业编号、员工表的部门编号

FOREIGN KEY:外键,在从表添加外键约束,用于引用主表中某列的值

③ 添加约束的时机:创建表时、修改表时。总而言之,是在添加数据之前

④ 约束的添加分类:列级约束、表级约束

六大约束 列级约束 表级约束
NOT NULL
PRIMARY KEY
FOREIGN KEY ☆,不支持,没有效果
DEFAULT
CHECK ☆,不支持
UNIQUE

⑤ 约束的语法

CREATE TABLE 表名(
	字段名 类型 列级约束,
    字段名 类型 列级约束,
    表级约束
);

⑥ 添加列级约束

CREATE TABLE stuinfo (
	id INT PRIMARY KEY,   #主键
	stuName VARCHAR (20) NOT NULL,  #非空
	gender CHAR CHECK (gender = '男' OR gender = '女'),  #检查约束,MySQL不支持
	seat INT UNIQUE,   #唯一约束
	age INT DEFAULT 18,   #默认约束
	majorId INT REFERENCES major(id)  #外键  没有效果
);

⑦ 添加表级约束

CREATE TABLE stuinfo (
	id INT ,  
	stuName VARCHAR (20),  
	gender CHAR,  
	seat INT,   
	age INT,   
	majorId INT  
    constraint pk primary(id),
    constraint uq unique(seat),
    constraint ck check(gender = '男' OR gender = '女'),
    constraint fk_stuinfo_major foreign key(majorId) references major(id)
    #语法:【constraint 约束名】 约束类型(字段名)
);


标准写法

CREATE TABLE stuinfo (
	id INT PRIMARY KEY,  
	stuName VARCHAR (20) NOT NULL,  
	gender CHAR CHECK (gender = '男' OR gender = '女'),  
	seat INT UNIQUE,   
	age INT DEFAULT 18,   
	majorId INT,
	CONSTRAINT pk_stuinfo_major FOREIGN KEY(majorId)REFERENCES major(id)
);

⑧ 查看表中的所有索引,包括主键、外键、唯一

SHOW INDEX FROM 表名;

面试题

1、主键和唯一的对比?

保证唯一性 是否允许为空 一个表中有几个 是否允许组合
主键(primary key) 至多一个 组合主键,不推荐
唯一(unique) ☆(只允许一个值为空) 可以多个 组合唯一键,不推荐

组合主键:

constraint pk primary key(id,name);  #将Id和name组合为主键,只有插入数据id和name都一样时,报错

2、外键:

① 要求在从表设置外键关系

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

③ 主表的关联列必须是一个key(一般是主键或唯一,外键也行(无意义))

④ 插入数据时,先插入主表,再插入从表;删除数据时,先删从表,再删除主表

3、修改表时添加约束

#语法
ALTER TABLE 表名 modify column 列名 类型 约束;
ALTER TABLE 表名 ADD 【CONSTRAINT 约束名】约束类型 (字段名) 【references 主表(字段)】
#案例 添加非空约束
ALTER TABLE stuinfo modify column stuName varchar(20) not NULL;
#案例 添加默认约束
ALTER TABLE stuinfo modify column age int default 18;
#案例 添加主键、唯一
#方式1:列级约束
ALTER TABLE stuinfo modify column id int primary key;
#方式2:表级约束(了解)
ALTER TABLE stuinfo ADD PRIMARY KEY(id);

4、修改表时删除约束

#案例1:删除非空约束
ALTER TABLE stuinfo modify column stuName varchar(20) 【null】;
#案例2:删除默认约束
ALTER TABLE stuinfo modify column age int;
#删除主键
ALTER TABLE stuinfo drop primary key;   #只存在一个主键,不用加列名
#删除唯一键
ALTER TABLE stuinfo DROP INDEX seat;
#删除外键约束
ALTER TABLE stuinfo drop foreign key fk_stuinfo_major;#外键名写法:fk_从表名_主表名

标识列

① 含义:又称自增长列。可以不用手动插入值,系统提供默认的序列值

② 创建表示设置标识列

#创建表示设置标识列
CREATE TABLE book_author (
	id INT PRIMARY KEY AUTO_INCREMENT,
	authorNAME VARCHAR (20) NOT NULL
);
#如果想修改起始值,手动插入一条,然后执行下面插入语句
#插入数据时
#方式1:
INSERT INTO book_author VALUES (NULL, 'mary');
#方式2:
INSERT INTO book_author(name) VALUES ('mary');   #同插入null时一致,都省略不写
	

③ 设置自增长的步长【也可以修改起始值,见上】

SET auto_increment_increment=3;

④ 标识列必须和主键搭配使用? 不一定,但要求是一个key

⑤ 一个表可以有几个标识列?类型是否限制? 最多有一个;类型限制,只能是数值型,一般为int

⑥ 修改表时设置\删除标识列

#修改表时设置标识列
ALTER TABLE 表名 modify column 列名 类型 约束 AUTO_INCREMENT;  
#修改表时删除标识列,直接去掉就行
ALTER TABLE 表名 modify column 列名 类型 约束;

补充 ☆☆☆

以下两种方式删除主表中的记录,这样删除的原因:因为主表与从表通过外键连接,当删除主表的数据时,从表也会受到影响

#传统的添加外键的方式:
ALTER TABLE 表名 ADD CONSTRAINT foreign key(字段名) references 主表(字段);
#删除主表中的数据时,先删从表,后删主表
#删除方式一:添加级联删除 的外键
ALTER TABLE 表名 ADD CONSTRAINT foreign key(字段名) references 主表(字段) on DELETE CASCADE;
#此方式在删除主表关联列的某个值的那一行数据时,会将从表中外键列对应的值的行全部删除
#删除方式二:级联置空
ALTER TABLE 表名 ADD CONSTRAINT foreign key(字段名) references 主表(字段) on DELETE SET NULL;
#此方式在删除主表的关联列的某个值对用的那行数据时,会将从表中外键列对用的该值的位置设置为NULL

6、TCL语言的学习

6.1、事务的介绍

① 概念:一个或一组sql语句组成的一个执行单元,在这个单元中,每条语句相互依赖,要么全部执行,要么不执行。整个单元作为一个不可分割的整体,如果其中一条执行失败或产生错误,整个单元将会回滚。所有受影响的事务回到开始的状态

② 案例:转账

6.2、事务的ACID属性☆☆☆

① 原子性:指事务是一个不可分割的工作单位,事务的操作要么不发生,要么都发生

② 一致性:事务必须使数据库从一个一致性状态变换到另外一个一致性状态。例如:转账,转账之前和转账之后的总金额不变

③ 隔离性:一个事务的执行不受其他事务的干扰,即一个事务内部的操作及使用的数据对并发的其他事物是隔离的,并发执行的各个事务互不干扰

④ 持久性:一个事务一旦提交,对数据库的改变是永久性的,接下来的其他操作和数据库故障不应该对其有任何影响

6.3、事务的使用

6.3.1 事物的创建

① 隐式事务:事务没有明显的开启和结束的标记。例如insert、update、delete语句

② 显式事务:事物具有明显的开启和结束的标记。前提:必须先设置自动提交功能的禁用

#设置自动提交功能的禁用
set autocommit=0;

#查看当前自动提交功能状态
SHOW VARIABLES LIKE 'autocommit';

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-L6LdzR8v-1682066831858)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20211115195444913.png)]

③ 显式事务的创建:

​ 步骤1:开启事务

#设置自动提交功能的禁用
set autocommit=0;	
#开启事务
start transaction;	#可选的

​ 步骤2:编写事务的SQL语句

#编写事务的sql语句(select、insert、update、delete)
语句1;
语句2;
......


​ 步骤3:结束事务

commit;	#提交事务
rollback;	#回滚事务    相当于表中数据没变

6.3.2 事务的隔离

① 并发问题

对于同时运行的多个事务,当这些事务访问数据库中相同的数据时,如果没有采取必要的隔离措施,就会导致各种并发问题:

脏读:对于两个事务T1、T2,T1读取了已经被T2更新但还没提交的字段;之后,若T2回滚,T1读取的内容是临时无效的

不可重读性:对于两个事务T1、T2,T1读取了一个字段,然后T2更新了这个字段,之后,T1在读取这个字段,值不同了

幻读:对于两个事务T1、T2,T1从一个表中读取了一个字段,然后T2在该表中插入一些行,之后,T1在读取同一个表,就会多出几行

② 隔离级别

隔离级别 描述
READ UNCOMMITTED(读取为未提交数据) 允许事务读取未被其他事务提交的变更,幻读、不可重读、脏读都会出现
READ COMMITTED(读已提交数据) 只允许事务读取已经被其他事务提交的变更,可以避免脏读,但幻读、不可重读可能出现
REPEATABLE READ(可重复读) 确保事务可以从一个字段中读取相同的值,在这个事务持续期间,禁止其他事务对这个字段更新,可以避免脏读和不可重复读,但幻读仍存在
SERIALIZABLE(串行化) 确保事务可以从一个表中读取相同的行,在这个事务持续期间,禁止其他事务对该表执行插入、更新、删除操作。所有并发问题都可以避免,但性能低下

③ 解决方案

​ 一个事务与其他事务隔离的程度称为隔离级别。数据库规定了多种事务隔离级别,对应不同的干扰程度,级别越高,一致性越好,但并发性越弱

#管理员身份进入命令提示符
SELECT @@transaction_isolation;   #该命令是MySQL 8.0以后的

#设置当前MySQL连接的隔离级别
set session transaction isolation level read committed;
#设置数据库系统的全局隔离级别
set global transaction isolation level read committed;

④ 演示savepoint的使用

SET autocommit=0;
START TRANSACTION;
DELETE FROM account WHERE id=25;
savepoint a;      #设置保存点,搭配 ROLLBACK
DELETE FROM account WHERE id=20;
ROLLBACK to a;     #回滚到保存点     含义是 20号没删,25删除了

6.4、视图的介绍

① 含义:虚拟的表,和普通的表一样使用;mysql5.1版本出现的新特性,是通过表动态生成的数据

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

③ 示例:

CREATE VIEW 视图名
AS
sql语句

6.5、视图的创建

#查询邮箱包含a的员工名、部门名和工种信息
#①创建视图
CREATE VIEW myv1
AS
SELECT
	e.last_name,
	d.department_name,
	j.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;
#②使用视图
SELECT
	*
FROM
	myv1
WHERE
	last_name LIKE '%a%';



视图的优点:

① 重写SQL语句

② 简化复杂的sql操作,不必知道它的查询细节

③ 保护数据,提高安全性

6.6、视图的修改

修改方式1:

create or replace view 视图名(不变)
AS
查询语句;

修改方式2:

ALTER view 视图名(不变)
as
查询语句;

6.7、视图的查看

#删除视图
drop view 视图名,视图名,...;
#查看视图结构
#方式1
DESC 视图名;
#方式2
SHOW CREATE VIEW 视图名;   #表也可以


6.8、视图的更新

① 增、删、改

#创建视图
CREATE
OR REPLACE VIEW myv1 AS SELECT
	last_name,
	email
FROM
	employees;
#插入
INSERT INTO myv1 VALUES('张飞', '[email protected]');
#修改
UPDATE myv1 SET last_name ='张无忌' WHERE last_name='张飞';
#删除
DELETE FROM myv1 WHERE last_name='张无忌';

② 视图的可更新性和视图中查询的定义有关系,以下视图的类型不支持更新

​ a;包含以下关键字的查询语句:分组函数、distinct、group by、having、union或者union all

​ b:常量视图,例如:

create or replace 'john' name;

​ c:SELECT 中包含子查询、join、from一个不能更新的视图

​ d:where子句中的子查询引用了from句子中的表

CREATE OR REPLACE VIEW myv6
AS
select last_name,salary,email
from
enployees
where employee_id IN(
	select manager_id
	FROM EMPLOYEES
WHERE manager_id is NOT NULL);

③ 视图和表的对比

创建语句 是否实际占用物理空间 使用
视图 CREATE VIEW 视图名 没有,只是保存了SQL逻辑 增删改查,一般不能增删改查
CREATE TABLE 表名 占用 增删改查

6.9、特别注意

TRUNCATE 和 DELETE 在事务使用时的区别?

DELETE在事务中对表的删除支持回滚;TRUNCATE不支持回滚

7、变量

7.1、变量的介绍

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1BoNeeUR-1682066831858)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20211116213926690.png)]

7.2、系统变量

① 说明:变量由系统定义,不是用户定义,属于服务器层面

② 使用的语法:

#1、查看所有的系统变量
SHOW GLOBAL(全局)|【SESSION(会话)】 VARIABLES;
#2、查看满足条件的部分系统变量
SHOW GLOBAL【SESSION】 variables like '%char%';
#3、查看指定的某个系统变量的值
select @@global.系统变量名;
select @@【SESSION】.系统变量名;
#4、为某个系统变量赋值
#方式1:
set global 系统变量名 = 值;
set 【session】 系统变量名 =值;   #设置事务的隔离级别
#例如:创建事务时,开启事务时,禁用自动提交功能
set autocommit=0;
#方式2:
set @@global|【session】.系统变量名=值;

③ 注意:

如果是全局级别,需要加GLOBAL;如果是会话级别,则需要加SESSION。不写,默认SESSION

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

​ 会话变量作用域:仅仅针对于当前会话连接有效

7.3、自定义变量

① 说明:变量是用户自定义的,不是由系统

② 用户变量作用域:针对于当前会话连接有效的,同系统变量的会话变量一致

③ 用户变量使用步骤:

#声明并初始化
SET @用户变量名=值;
#同下
SET @用户变量名:=值;
#同下
select @用户变量名:=值;
#例如:
SET @name=100;

#赋值(更新用户变量的值)
#方式1:通过SET或SELECT
SET @用户变量名=值;
SET @用户变量名:=值;
select @用户变量名:=值;
#方式2:通过SELECT INTO
SELECT 字段 INTO 变量名
FROM 表;
#例如:
SELECT COUNT(*) INTO @name
from employees;    # name用户变量的值修改为employees表的行数

#查看用户变量的值
SELECT @用户变量名;
#例如:
select @name;


④ 应用场景:应用在任何地方,也就是 begin end里面或者外面


⑤ 局部变量作用域:仅仅在定义它的begin end 中有效

⑥ 局部变量的使用步骤:

#声明
DECLARE 变量名 类型;
#声明初始化
DECLARE 变量名 类型 DEFAULT 值;

#赋值
#方式1:通过SET或SELECT
SET 局部变量名=值;
SET 局部户变量名:=值;
select @局部变量名:=值;
#方式2:通过SELECT INTO
SELECT 字段 INTO 局部变量名
FROM 表;

#使用
select 局部变量名;

⑦ 应用场景:begin end 的第一句话 ☆☆☆


⑧ 用户变量和局部变量对比:

作用域 定义和使用的位置 语法
用户变量 当前会话 会话的任何地方 必须加@,不限定类型
局部变量 BEGiN END 只能在GEGIN END中,且为第一句话 一般不加@,需要限定类型

⑨ 案例练习

#声明两个变量并赋初始值,求和,打印
#1、用户变量
SET @m =1;
SET @n =2;
SET @sum=@m+@n;
SELECT @sum;

#2、局部变量   执行结果 error,局部变量的使用在GEBIN END中
DECLARE m INT DEFAULT 1;
DECLARE n INT DEFAULT 2;
DECLARE sum INT;
SELECT sum;

8、存储过程和函数

8.1、概述

① 存储过程和函数:类似于java中的方法

② 好处:提高代码的重用性;简化操作

8.2、存储过程

① 存储过程:一组预先编译好的SQL语句的集合,理解成批处理语句

② 优点:提高代码的重用性;简化操作;减少编译次数并且减少了和数据库服务器连接的次数,提高了效率

③ 创建语法(基于Mysql 5.0版本操作)

DELIMITER 结束标记   #(注意此时没有分号,如果有则使用时一定加上)
CREATE PROCEDURE 存储过程名(参数列表)BEGIN
	存储过程体(一般合法的SQL语句)	
END 结束标记
#注意:
#1、参数列表包含三部分:参数模式 参数名 参数类型,例如:
IN stuName VARCHAR(20)
/*2、参数模式:
IN:该参数可以作为输入,也就是说该参数需要调用方传入值
OUT:该参数可以作为输出,也就是该参数可以作为返回值
INOUT:该参数既可以作为输入又可以作为输出,也就是说该参数既可以作为传入值,又可以作为返回值
*/
#3、如果存储过程仅仅以一句话,BEGIN END可以省略
#4、存储过程体中的每条SQL语句的结尾要求必须加分号;存储过程的结尾可以用DELIMITER 重新设置
	#语法
	DELIMITER 结束标记
	#案例
	DELIMITER $


# MySQL8.0 可以不定义结束标志,END以分号结尾

④ 调用语法

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

⑤ 存储过程的定义和调用案例(MySQL 8.0举例)

#1、空参列表
#案例:插入到job_grades表中五条记录
#存储过程创建
CREATE PROCEDURE myp1 ()
BEGIN
	INSERT INTO job_grades (
		grade_level,
		lowest_sal,
		highest_sal
	)
VALUES
	('A', 2000, 3999),('B', 4000, 6999),
	('C', 7000, 9999);
END;
#存储过程调用
CALL myp1();

#2、创建带out模式的存储过程
#案例:根据女神名返回男神名
#创建存储过程
CREATE PROCEDURE myp5 (
	IN beautyName VARCHAR (20),
	OUT boyName VARCHAR (20)
)
BEGIN
	SELECT
		bo.boyName INTO boyName
	FROM
		beauty b
	INNER JOIN boys bo ON b.boyfriend_id = bo.id
	WHERE
		b.name = beautyName;
END;
#调用,首先定义变量,由于穿出参数在begin end 外面,于是定义用户变量
SET @bName='john';    # 用户变量必须初始化 ,可省略不写
CALL myp5('柳岩',@bName);
SELECT @bName;

#3、创建多个参数的存储过程
#案例2:根据女神名,返回对应的男神名和魅力值
#创建存储过程
CREATE PROCEDURE myp6 (
	IN beautyName VARCHAR (20),
	OUT boyName VARCHAR (20),
	OUT copyValue INT
)
BEGIN
	SELECT
		bo.boyName,
		bo.userCP INTO boyName,
		copyValue                        #SELECT INTO用法:SELECT A,B INTO C,D
	FROM
		beauty AS b
	INNER JOIN boys AS bo ON b.boyfriend_id = bo.id
	WHERE
		b. NAME = beautyName;
END;
#调用
SET @Boyname;
SET @userCP;
CALL myp6 ('柳岩' ,@Boyname ,@userCP);
#查看
SELECT
	@BoyName ,@userCP;

#4、创建带inout模式参数的存储过程
#案例1:传入a,b两个值,最终返回a,b翻倍的值
CREATE PROCEDURE myp7 (INOUT a INT, INOUT b INT)
BEGIN
SET a = a * 2;
SET b = b * 2;
END;
#定义用户变量
SET @a=10;
SET @b=20;
#由于使用inout有返回值,所以传入定义的变量,定义时初始化
CALL myp7(@a,@b);
SELECT @a,@b;

⑥ 存储过程的删除

drop procedure 存储过程名;		#每次只能

⑦ 查看存储过程信息

SHOW create PROCEDURE 存储过程名;   #表和视图一样可以

8.3、函数

① 函数的创建:

CREATE FUNCTION 函数名(参数列表)RETURNS 返回类型
BEGIN
	函数体
END
#注意
#1、参数列表包含两部分:参数名 参数类型
#2、函数体:肯定有return语句,如果没有会报错;如果return语句没有放在函数体最后也不会报错,但不建议
#3、当函数体中只有一句话,则可以省略begin end
#4、MySQL5.7使用delimiter语句设置结束标记,8.0不用

② 调用语法:与系统提供的函数调用方法一致

SELECT 函数名(参数列表)    #执行完函数体的内容,并显示返回值

③ 函数的查看与删除

SHOW CREATE FUNCTION 函数名;
drop function 函数名;

④ 函数的使用

#案例1:创建函数,实现传入两个float,返回二者的和


8.4、总结

① 函数与存储过程的区别

返回值个数 应用
函数 0个或多个返回 适合批量插入、批量更新
存储过程 只能有1个返回 适合做数据处理后返回一个结果或查询一个值

9、流程控制结构

9.1、概述

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

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

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

9.2、分支结构

#1、if函数
#功能:实现简单的双分支
#语法
SELECT IF(表达式1,表达式2,表达式3)
#应用:任何地方

#2、case结构
#情况1:类似于java中switch语句,一般用于实现的等值判断
#语法
CASE 变量|表达式|字段
WHEN 要判断的值 THEN 返回的值1
WHEN 要判断的值 THEN 返回的值2
......
else 返回值n
END
#情况2:类似于java中多重if,类似于区间判断
#应用:可以作为表达式,嵌套在其它语句中使用,可以放在任何地方,BEGIN END中或外面;
#作为独立语句使用,只能放在BEGIN END中

#作为独立语句使用,只能放在BEGIN END中
#创建存储过程,根据传入的成绩,来显示等级,比如:90-100,A;80-90,B;60-80,C;否则,D
CREATE PROCEDURE myp1(IN score INT)
BEGIN
CASE
WHEN score>=90 && score<=100 THEN SELECT 'A';   #是一条语句,作为独立语句
WHEN score>=80 THEN SELECT 'B';
WHEN score>=60 THEN SELECT 'C';
ELSE SELECT 'D';
END CASE;
END;

CALL myp1(78);

#3、if结构,实现多重分支
#语法:
if 条件1 then 语句1;
elseif 条件2 then 语句2;
......
【else 语句n;】
end if;
#应用场合:只能存储在GEGIN END 中
#案例:创建函数,根据传入的成绩,来显示等级,比如:90-100,返回A;80-90,返回B;60-80,返回C;否则,返回D


9.3、循环结构

① 分类:while、loop、repeat【只能放在 begin end 里头】

② 循环控制:iterate类似于continue,结束本次循环,继续下一次;leave 类似于break,跳出循环

#1、while
/*语法:
【标签:】while 循环条件 do
	循环体;
	end while【标签】;
	*/

#2、loop
/*语法:
【标签】loop
	循环体;
	end loop【标签】;
可以模拟简单的死循环
*/

#3、repeat
/*语法:
【标签:】repeat
	循环体;
	until 结束循环的条件
	end repeat 【标签】;
*/

#案例:批量插入,根据次数插入到admin表中多条记录
CREATE PROCEDURE myp3(IN insertCount INT)
BEGIN
declare i INT DEFAULT 1;
a:WHILE i<=insertCount DO
INSERT INTO admin(username,password)VALUES(CONCAT('rose',i),'6666');
set i=i+1;
END WHILE a;    #a为标签
END

CALL myp3(10);

#案例:添加循环控制语句 leave
#批量插入数据,如果次数大于20则停止
CREATE PROCEDURE myp4(IN insertCount INT)
BEGIN
DECLARE i INT DEFAULT 1;
a:while i<=insertCount DO
INSERT INTO admin(username,PASSWORD)VALUES(CONCAT('xiaohua',i),'22324234');
IF i>=20 THEN LEAVE a;            #加上循环控制,必须使用标签
END IF;
SET i=i+1;
END WHILE a;
END

CALL myp4(100);
#案例:添加循环控制语句 leave
##批量插入数据,只插入偶数
TRUNCATE table admin;

CREATE PROCEDURE myp10(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),'22324234');
END WHILE a;
END

CALL myp10(100);

#案例:已知表stringcontent,其中字段:id 自增长,content varchar(20),向该表插入指定个数的,随机字符串

常见问题

问题1:1055

错误提示:[Err] 1055 - Expression #1 of ORDER BY clause is not in GROUP BY clause and contains nonaggregated column 'information_schema.

PROFILING.SEQ’ which is not functionally dependent on columns in GROUP BY clause;

this is incompatible with sql_mode=only_full_group_by

解决方案1:先运行以下代码

show variables like "sql_mode";
set sql_mode='';
set sql_mode='NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES';

如果不是解决方案不行,参考下面

#按多个字段分组
#案例:查询每个部门每个工种的员工的平均工资
#按多个字段分组
#案例:查询每个部门每个工种的员工的平均工资
SELECT
	avg(salary),
	department_id,
	job_id
FROM
	employees
GROUP BY
	department_id;
#以上代码同样会出现1055错误信息,[Err] 1055 - Expression #3 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'myemployees.employees.job_id' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by,原因是:select中的表达式3,也就是job_id,不在GROUP BY子句中,包含未聚合列 'myemployees.employees.job_id',在功能上不依赖GROUP BY子句中的列。
#解决方案:保持SELECT除分组函数以外的表达式与GROUP BY中的表达式保持一致

问题2:1052

错误提示:[Err] 1052 - Column ‘job_id’ in field list is ambiguous(不明确的)

错误原因1:

#查询员工名、工种名、工种号
SELECT
	last_name,
	employees.job_id,                #由于employees与jobs中都有job_id,所以需要一个表名限定一下,两个表名都可以;
	job_title
FROM
	employees,
	jobs
WHERE
	employees.job_id = jobs.job_id;

问题3:1054

错误提示:[Err] 1054 - Unknown column ‘jobs.job_id’ in ‘where clause’

错误原因1:如果为表取了别名,就不可以使用原来表明限定

#查询员工名、工种名、工种号
SELECT
	last_name,
	e.job_id,
	job_title
FROM
	employees AS e,
	jobs as j
WHERE
	e.job_id = jobs.job_id;   #如果为表取了别名,就不可以使用原来表明限定

解决方案:将e.job_id = jobs.job_id; 改为e.job_id = j.job_id;

问题4:1264

错误提示:Out of range value for column

错误原因:存入的数据超出该字段约束类型可表示的范围

问题5:1418

错误提示:This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA in its declaration and

问题原因1:

/*这是我们开启了bin-log, 我们就必须指定我们的函数是否是

1 DETERMINISTIC 不确定的

2 NO SQL 没有SQl语句,当然也不会修改数据

3 READS SQL DATA 只是读取数据,当然也不会修改数据

4 MODIFIES SQL DATA 要修改数据

5 CONTAINS SQL 包含了SQL语句

其中在function里面,只有 DETERMINISTIC, NO SQL 和 READS SQL DATA 被支持。如果我们开启了 bin-log, 我们就必须为我们的function指定一个参数。*/

解决方案1:

#查看该变量是否为ON,如果不是,在当前会话框修改变量值
show variables like 'log_bin_trust_function_creators';
#修改语句
 set global log_bin_trust_function_creators=1;
 #上述修改。如果重启MySQl会消失,所以在my.ini配置文件中添加
 log_bin_trust_function_creators=1

关键字

set用处

① 向表中插入数据

insert into 表名
set 列名=值,...

② 修改表中数据

update 表1 别名
inner |left|right join 表2 别名
on 连接条件
set 列=值,...
where 连接条件
and 筛选条件;

③ 作为字段的约束类型,集合

你可能感兴趣的:(数据库,mysql,java,python,笔记)