2022-5-26
MySQL学习
文章目录
1、MySQL8.0的安装
1.1、MySQL的4大版
- MySQLCommunityServer社区版本,开源免费,自由下载,但不提供官方技术支持,适用于大多数普通用户。
- MySQLEnterpriseEdition企业版本,需付费,不能在线下载,可以试用30天。提供了更多的功能和更完备的技术支持,更适合于对数据库的功能和可靠性要求较高的企业客户。
- MySQLCluster集群版,开源免费。用于架设集群服务器,可将几个MySQLServer封装成一个Server。需要在社区版或企业版的基础上使用。
- MySQLClusterCGE高级集群版,需付费。
- 目前最新版本为8.0.27,发布时间2021年10月。此前,8.0.0在2016.9.12日就发布了。
- 本课程中使用8.0.26版本。
- 此外,官方还提供了MySQLWorkbench(GUITOOL)一款专为MySQL设计的图形界面管理工具。
MySQLWorkbench又分为两个版本,分别是社区版(MySQLWorkbenchOSS)、商用版(MySQLWorkbenchSE)。
1.2、软件下载
第一步:软件下载
第二歩:安装软件
- 选择你要的版本
- 把版本导入过去:
- 选择安装位置
- 安装完成后:
- 对MySQL进行配置一直next到以下界面:
- 并保证配置与此界面一样:
- 点击finsh完成配置
第三歩:配置环境变量
- 软件安装完成后。此时仍不是内部命令,但是不要慌,下面来配置
- 输入你刚刚复制的bin目录
- 确认后,重新win+r打开命令行
安装完成
2022-5-26
2、SQL背景概念
2.1、SQL背景知识
- 1946年,世界上第一台电脑诞生,如今,借由这台电脑发展起来的互联网已经自成江湖。在这几十年里,无数的技术、产业在这片江湖里沉浮,有的方兴未艾,有的已经几幕兴衰。但在这片浩荡的波动里,有一门技术从未消失,甚至“老当益壮”,那就是SQL。
- 45年前,也就是1974年,IBM研究员发布了一篇揭开数据库技术的论文《SEQUEL:一门结构化的英语查询语言》,直到今天这门结构化的查询语言并没有太大的变化,相比于其他语言,SQL的半衰期可以说是非常长了。
- 不论是前端工程师,还是后端算法工程师,都一定会和数据打交道,都需要了解如何又快又准确地提取自己想要的数据。更别提数据分析师了,他们的工作就是和数据打交道,整理不同的报告,以便指导业务决策。
- SQL(StructuredQueryLanguage,结构化查询语言)是使用关系模型的数据库应用语言,与数据直接打交道,由IBM上世纪70年代开发出来。后由美国国家标准局(ANSI)开始着手制定SQL标准,先后有SQL-86,SQL-89,SQL-92,SQL-99等标准。
- SQL有两个重要的标准,分别是SQL92和SQL99,它们分别代表了92年和99年颁布的SQL标准,我们今天使用的SQL语言依然遵循这些标准。
- 不同的数据库生产厂商都支持SQL语句,但都有特有内容。
2.2、SQL分类
-
DDL(DataDefinitionLanguages、数据定义语言),这些语句定义了不同的数据库、表、视图、索引等数据库对象,还可以用来创建、删除、修改数据库和数据表的结构。
- 主要的语句关键字包括:CREATE、DROP、ALTER等。
-
DML(DataManipulationLanguage、数据操作语言),用于添加、删除、更新和查询数据库记录,并检查数据完整性。
- 主要的语句包括关键字:INSERT、DELETE、UPDATE、SELECT等。
- select是SQL语言的基础,最为重要。
-
DCL(DataControlLanguage、数据控制语言),用于定义数据库、表、字段、用户的访问权限和
安全级别。
主要的语句关键字包括GRANT、REVOKE、COMMIT、ROLLBACK、SAVEPOINT等。 -
因为查询语句使用的非常的频繁,所以很多人把查询语句单拎出来一类:DQL(数据查询语言)。
还有单独将COMMIT、ROLLBACK取出来称为TCL(TransactionControlLanguage,事务控制语言)。
2.3、SQL语言的规范
①、SQL编写规范
- SQL可以写在一行或者多行。为了提高可读性,各子句分行写,必须时使用缩进。
- 每条命令以;或\g或\G结束。
- 关键字不能被缩写也不能分行。
- 关于标点符号:
- 必须保证所有(),单引号,双引号是成对结束的。
- 必须使用英文状态下的半角输入方式
- 字符串和日期时间类型的数据可以使用单引号(‘’)表示。
- 列的别名,尽量使用双引号(“”,而且不建议省略as)
②、SQL大小写规范
- mysql在Windows环境下是大小写不敏感的
- mysql在Linux环境下是大小写敏感的
- 数据库名,表名,表的别名,变量名是严格区分大小写的。
- 关键字。函数名、列名(字段名)、列的别名(字段名)是忽略大小写的。
- 推荐采用的统一的书写规范
- 数据库名、表名、表别名、字段名、字段别名等都小写
- SQL关键字、函数名、绑定变量等都大写
③、注释
- 可以使用如下格式的注释结构
- 单行注释:#注释文字(MySQL特有的方式)
- 单行注释:– 注释文字(-- 后面必须包含一个空格)
- 多行注释:*/* 注释文字 /
④、命名规则
- 数据库、表名不得超过30个字符,变量名限制为29个
- 必须只能包含A–Z,a–z,0–9,_共63个字符
- 数据库名、表名、字段名等对象名中间不要包含空格
- 同一个MySQL软件中,数据库不能同名;同一个库中,表不能重名;同一个表中,字段不能重名
- 必须保证你的字段没有和保留字、数据库系统或常用方法冲突。如果坚持使用,请在SQL语句中使用`(着重号)引起来
- 保持字段名和类型的一致性,在命名字段并为其指定数据类型的时候一定要保证一致性。假如数据
类型在一个表里是整数,那在另一个表里可就别变成字符型了
举例子:
-
#以下两句是一样的,不区分大小写 show databases; SHOW DATABASES; #创建表格 #createtablestudentinfo(...);#表名错误,因为表名有空格 create table student_info(...); #其中order使用``飘号,因为order和系统关键字或系统函数名等预定义标识符重名了 CREATE TABLE`order`( id INT, lname VARCHAR(20) ); select idas"编号",`name`as"姓名"from t_stu;#起别名时,as都可以省略 select idas编号,`name`as姓名 from t_stu;#如果字段别名中没有空格,那么可以省略"" select idas编号,`name`as姓名 from t_stu;#错误,如果字段别名中有空格,那么不能省略""
2.4、数据导入指令
-
在命令行客户端登陆MySQL,使用source指令导入
-
mysql>sourced:\mysqldb.sql
3、数据库,表的基本操作
3.1、数据库的基本操作
#1、-----查看数据库
show databases;
#2、-----删除数据库
drop database 库名;
#3、-----选择数据库
use 库名;
#4、-----创建数据库
create database zzj; #创建一个数据库名zzj的数据库,字符集为默认字符集
>create database IF
>NOT EXISTS RUNOOB
>default character set utf8
>collare utf8_general_ci;
#如果数据库不存在则创建,存在则不创建。(IF NOT EXISTS)
#创建RUNOOB数据库,并设定编码集为:utf8,校对规则为:utf8_general_ci;
#5、----查看数据库的字符集
show create database 库名;
#6、-----查看数据库的字符编码
show create database 库名 \G;
3.2、表的基本操作
#1、创建表(必须带字段)
cerate table 表名 (id int,sname char(20),age int,sex char(20));
#2、查看表
show tables;
#3、查看表的基本结构
desc 表名
#4、查看表的数据结构
show create table 表名 \G;
#5、查看表的数量
show tables;
#6、删除表
drop table 表名
#7、修改表名
alter table 表名1 rename to(可省) 表名2;
#8、增加字段
alter table 表名 add 字段(列名) 数据类型 first;→→【加字段在表头
alter table 表名 add 字段1(列名) 数据类型 after 字2→→【在字段2的后面加一个字段1)
alter table 表名 add 字段(列名) 数据类型→→→【默认加在末尾
#9、删除字段
alter table 表名 drop 列名(字段);
#10、修改字段中的数据类型
alter table 表名 modify 列表(字段)新的数据类型;
#11、修改字段的顺序
alter table 表名 modify 字段名 数据类型 first 或 after 列名(字段);
2022-5-27
4、基本select语句
4.1、用途:
- 用于从一个表或多个表中检索信息。
4.2、学习技巧
- 大处着眼,小处着手
4.3、SELECT…
-
SELECT 9; SELECT 9/2; SELECT 9,9/2 FROM DUAL; #DUAL是伪表
4.4、SELECT …FROM
-
SELECT employee_id,first_name,last_name #查找指定字段的数据 FROM employees; SELECT * FROM employees; #查询表的全部数据
4.5、列的别名
-
SELECT employee_id AS 学号,last_name AS 姓,first_name AS 名 FROM employees;
-
SELECT employee_id AS id,last_name "姓",first_name AS 名 FROM employees; //英文和中文都可以作为别名
4.6、去除重复行(DISTINCT)
-
例题1、查询员工表中一共有哪些部门
-
没加 DISTINC T的数据
-
SELECT department_id FROM employees;
-
-
加 DISTINCT 后的数据
-
SELECT DISTINCT department_id FROM employees;
-
-
4.7、空值运算符
-
null–>空值
-
null不等同于 0 和’ ’
-
null参与运算结果为null
-
查询员工年工资多少
-
SELECT employee_id,salary "月工资",salary * (1+commission_pct)*12 "年工资" FROM employees; #数据中有的年工资是null,因为有员工commission是null
-
SELECT employee_id,salary "月工资",salary * (1+IFNULL(commission_pct,0))*12 "年工资" FROM employees; #ifnull判断字段是否是null,不是null返回原值,是返回0
-
4.8、着重号
-
当你去的表名与保留字相同时,在你查询该表时,表名请用``包起来
-
SELECT * FROM `order`;
-
4.9、查询常数
-
SELECT 'zzj',employee_id FROM employees;
4.10、WHERE过滤条件
-
#查询学号小于105的 SELECT employee_id AS 学号,last_name AS 姓,first_name AS 名 FROM employees WHERE employee_id < 105;
-
#查询90号部门的员工信息 SELECT * FROM employees WHERE department_id = 90;
-
#查询员工姓为“King”的员工 SELECT * FROM employees WHERE last_name = 'King';
练习题:
-
#【题目】 #1.查询员工12个月的工资总和,并起别名为ANNUALSALARY SELECT salary * (1 + IFNULL(commission_pct,0)) * 12 "ANNUALSALARY" FROM employees; #2.查询employees表中去除重复的job_id以后的数据 SELECT DISTINCT job_id FROM employees; #3.查询工资大于12000的员工姓名和工资 SELECT first_name,last_name,salary FROM employees WHERE salary > 12000; #4.查询员工号为176的员工的姓名和部门号 SELECT first_name,last_name,department_id FROM employees WHERE employee_id = 176; #5.显示表departments的结构,并查询其中的全部数据 DESC departments; SELECT * FROM departments;
2022-5-28
5、运算符
5.1、算术运算符
-
加减法
-
SELECT 100,100+0,100-0,100+50,100+50-30,100+35.5,100-35.5 FROM DUAL; SELECT 100 + '1'; #结果是101,在java中是做链接的作用,但是在MySQL中是将字符串转化为数字来进行计算 SELECT 100 + 'A'; #结果是100,字符串转换不成功,可以用0代替 SELECT 100 + NULL; #结果是null
-
-
乘除法
-
SELECT 100,100*1,100*1.0,100/1.0,100/2,100+2*5/2,100/3,100 DIV 0 FROM dual;
-
SELECT -12%5,12 MOD 5 FROM DUAL;#取余结果的符号,与取余符号的前面这个数一样
-
5.2、比较运算符
-
1、概念:
- 比较运算符用来对表达式左边的操作数和右边的操作数进行比较,比较的结果为真则返回1,比较的结果为假则返回0,其他情况则返回null。
- 比较运算符经常被用来作为查询语句的条件来使用,返回符合条件的结果记录
-
2、等号运算符
-
等号运算符(=)判断等号两边的值、字符串或表达式是否相等,如果相等则返回1,不相等则返回0。
-
在使用等号运算符时,遵循如下规则:
- 如果等号两边的值、字符串或表达式都为字符串,则MySQL会按照字符串进行比较,其比较的是每个字符串中字符的ANSI编码是否相等。
- 如果等号两边的值都是整数,则MySQL会按照整数来比较两个值的大小。
- 如果等号两边的值一个是整数,另一个是字符串,则MySQL会将字符串转化为数字进行比较。
- 如果等号两边的值、字符串或表达式中有一个为NULL,则比较结果为NULL。
-
对比:SQL中赋值符号使用:=
-
例子:
-
SELECT 1 = 2,1 = '1','A' = 'A',1 = 3;
-
SELECT 'A' = 'A','A' = 'B'; #两边都是字符串时比较ASCII
-
-
安全等于:
-
可以对null值进行计算
-
SELECT NULL <=> NULL,1 <=> NULL;
-
-
-
3、不等于运算符:
-
不等于运算符(<>和!=)用于判断两边的数字、字符串或者表达式的值是否不相等,如果不相等则返回1,相等则返回0。不等于运算符不能判断NULL值。如果两边的值有任意一个为NULL,或两边都为NULL,则结果为NULL。SQL语句示例如下:
-
SELECT 1<>1,1!=2,'a'!='b',(3+4)<>(2+6),'a'!=NULL,NULL<>NULL;
-
-
4、符号比较运算符:
-
例子:
-
#IS NULL,IS NOT NULL,ISNULL------------------------------------- SELECT * FROM employees WHERE commission_pct IS NULL; #筛选commission_pct 为null 的员工信息 SELECT * FROM employees WHERE commission_pct IS NOT NULL; #筛选commission_pct 不为null 的员工信息 SELECT * FROM employees WHERE ISNULL(commission_pct); #筛选commission_pct 为null 的员工信息 #LEAST,GREATEST,-------------------------------------------------- SELECT LEAST(12,21); #12---返回最小值 SELECT GREATEST(12,21); #21---返回最大值 #BETWEEN。。。AND-------------------------------------------------- SELECT * FROM employees WHERE commission_pct BETWEEN 0.2 AND 0.25; #筛选commission_pct值在0.2-0.25之间的数据 #IN,NOT IN------------------------------------------------------- SELECT * FROM employees WHERE commission_pct IN (0.2,0.25); #筛选commission_pct值--在0.2-0.25之间的数据 SELECT * FROM employees WHERE commission_pct NOT IN (0.2,0.25); #筛选commission_pct值--不在0.2-0.25之间的数据
-
LIKE—模糊查询
-
#LIKE(模糊查询 %,_)------------------------------------------- SELECT * FROM employees WHERE last_name LIKE 'J%'; #查询last_name以 J开头 的员工信息 SELECT * FROM employees WHERE last_name LIKE '%J%'; #查询last_name中 含有J 的员工信息 SELECT * FROM employees WHERE last_name LIKE '_o%';#查询last_name中 第二个字符是o 的员工信息。
-
-
ESCAPE:
-
回避特殊符号的:使用转义符。例如:将[%]转为[ %]、[]转为[ ],然后再加上[ESCAPE‘$’]即可。
-
SELECT job_id FROM jobs WHERE job_id LIKE 'IT\_%'; #---IT_PROG
-
-
如果使用\表示转义,要省略ESCAPE。如果不是\,则要加上ESCAPE。
-
SELECT job_id FROM jobs WHERE job_id LIKE 'IT$_%' escape '$'; #---IT_PROG
-
-
-
REGEXP运算符
-
REGEXP运算符用来匹配字符串,语法格式为:exprREGEXP匹配条件。如果expr满足匹配条件,返回1;如果不满足,则返回0。若expr或匹配条件任意一个为NULL,则结果为NULL
-
1、‘^’匹配以该字符后面的字符开头的字符串。
-
2、‘$’匹配以该字符前面的字符结尾的字符串。
-
3、‘.’匹配任何一个单字符。
-
4、“[…]”匹配在方括号内的任何字符。例如,“[abc]”匹配“a”或“b”或“c”。为了命名字符的范围,使用一个‘-’。“[a-z]”匹配任何字母,而“[0-9]”匹配任何数字。
-
5、‘ * ’匹配零个或多个在它前面的字符。例如,“x*”匹配任何数量的‘x’字符,“[0-9]”匹配任何数量的数字,而“”匹配任何数量的任何字符。
-
SQL语句示例:
-
SELECT 'shkstart'REGEXP'^s','shkstart'REGEXP't$','shkstart'REGEXP'hk'; #判断字段是否与 s 开头,以 t 结尾,是否含有hk字符
-
SELECT'atguigu'REGEXP'gu.gu','atguigu'REGEXP'[ab]'; #1、判断。。。 #2、判断字段中是否 含有 a 或者 b
-
-
-
2022-5-28
5.3、逻辑运算符
逻辑运算符主要用来判断表达式的真假,在MySQL中,逻辑运算符的返回结果为1、0或者NULL。
3.1.逻辑非运算符
-
逻辑非(NOT或!)运算符表示当给定的值为0时返回1;当给定的值为非0值时返回0;当给定的值为NULL时,返回NULL。
-
SELECT NOT 1,NOT 0,NOT(1-1),NOT !1; #非0数时返回1,为0时返回0,-->NOT !1---!1 = 0,NOT 0 = 1 SELECT last_name,job_id FROM employees WHERE job_id NOT IN('IT_PROG','ST_CLERK','SA_REP'); #筛选不在这三个部门的员工信息
-
3.2.逻辑与运算符
-
逻辑与(AND或&&)运算符是当给定的所有值均为非0值,并且都不为NULL时,返回
1;当给定的一个值或者多个值为0时则返回0;否则返回NULL。-
SELECT 1 AND -1,0 AND NULL,1 AND NULL,0 AND 1;#当逻辑与中的条件有一处结果为0时整体为1 SELECT * FROM employees WHERE salary > 5000 AND last_name LIKE 'J%'; #筛选姓中以 J 开头,且工资大于5000的公司员工
-
5.4.逻辑或运算符
-
逻辑或(OR或||)运算符是当给定的值都不为NULL,并且任何一个值为非0值时,则返回1,否则返回0;当一个值为NULL,并且另一个值为非0值时,返回1,否则返回NULL;当两个值都为NULL时,返回NULL。
-
SELECT * FROM employees WHERE department_id = 10 or department_id = 20; #筛选 10号部门 或者 20号部门 的公司员工 #查询基本薪资不在9000-12000之间的员工编号和基本薪资 SELECT employee_id,salary FROM employees WHERE NOT(salary>=9000 AND salary<=12000); SELECT employee_id,salary FROM employees WHERE salary<9000 OR salary>12000; SELECT employee_id,salary FROM employees WHERE salary NOT BETWEEN 9000 AND 12000;
-
5.5、逻辑异或运算符
-
逻辑异或(XOR)运算符是当给定的值中任意一个值为NULL时,则返回NULL;如果两个非NULL的值都是0或者都不等于0时,则返回0;如果一个值为0,另一个值不为0时,则返回1。
-
SELECT 1 XOR -1,1 XOR 0,0 XOR 0,1 XOR NULL,1 XOR 1 XOR 1,0 XOR 0 XOR 0;
-
select last_name,department_id,salary FROM employees WHERE department_id in(10,20) XOR salary>8000; #两边的表达式一真一假时数据保留
-
5.6、位运算符
5.7、运算符的优先级
2022-5-30
6、排序与分页
- 在没有使用排序操作时,默认返回的数据是按照添加时的顺序显示
6.1、排序规则
- 使用ORDER BY子句排序
- ASC:升序
- DESC:降序
- ORDER BY 在 SELECT 语句的结尾。
6.2、单列排序
-
例子:
-
SELECT salary FROM employees WHERE salary > 5000 ORDER BY salary; //不写默认升序排序 ORDER BY salary ASC; //升序排序 #----where要声明在from之后,order之前
-
SELECT salary*12 annsal FROM employees WHERE salary > 5000 ORDER BY annsal ASC; #-----别名只能在 ORDER BY 里面使用,不能在 WHERE 中使用---因为SQL语句的执行过程是先from再where然后在select最后在order的
-
6.3、多列排序
-
可以使用不在SELECT列表中的列排序。
-
在对多列进行排序的时候,首先排序的第一列必须有相同的列值,才会对第二列进行排序。如果第
一列数据中所有值都是唯一的,将不再对第二列进行排序。 -
例子:
-
SELECT department_id,salary*12 annsal FROM employees ORDER BY department_id,annsal ASC;
-
6.4、分页
-
1、背景:
- 背景1:查询返回的记录太多,查看起来很不方便,怎么样能够实现分页查询
- 背景2:表中有4条数据,我们只要显示第2,3条数据怎么办。
-
2、实现规则
-
分页原理—所谓分页显示,就是将数据库中的结果集,一段一段显示出来需要的条件
-
MySQL中使用limit实现分页
-
格式:
-
limit[位置偏移量,] 行数
-
-
第一个“位置偏移量”参数指示MySQL从哪一行开始显示,是一个可选参数,如果不指定“位置偏移量”,将会从表中的第一条记录开始(第一条记录的位置偏移量是0,第二条记录的位置偏移量是1,以此类推);第二个参数“行数”指示返回的记录条数。
-
#----查询,展示前十条数据 SELECT * FROM employees -- LIMIT 0,10 ; #左闭右开 LIMIT 10 ; #----查询根据工资从小到大排序后的 前十条 SELECT * FROM employees -- LIMIT 0,10 ; ORDER BY salary ASC LIMIT 10 ; #前十条记录 #----每页显示20条数据,此时显示第三页 SELECT * FROM employees LIMIT 40,20; #----每页显示20条数据,此时显示第二页 SELECT * FROM employees LIMIT 20,20;
-
-
ySQL8.0中可以使用“LIMIT3OFFSET4”,意思是获取从第5条记录开始后面的3条记录,和“LIMIT4,3;”返回的结果相同。
-
分页显式公式:(当前页数-1)*每页条数,每页条数 SELECT*FROMtable LIMIT(PageNo-1)*PageSize,PageSize;
-
-
注意:LIMIT子句必须放在整个SELECT语句的最后!
-
使用LIMIT的好处
- 约束返回结果的数量可以减少数据表的网络传输量,也可以提升查询效率。如果我们知道返回结果只有1条,就可以使用LIMIT1,告诉SELECT语句只需要返回一条记录即可。这样的好处就是SELECT不需要扫描完整的表,只需要检索到一条符合条件的记录即可返回。
-
6.5、扩展
2022-5-31
7、多表查询
7.1、概念:
- 多表查询,也称为关联查询,指两个或更多个表一起完成查询操作。
- 前提条件:这些一起查询的表之间是有关系的(一对一、一对多),它们之间一定是有关联字段,这个关联字段可能建立了外键,也可能没有建立外键。比如:员工表和部门表,这两个表依靠“部门编号”进行关联。
7.2、进行多表查询的错误案例
-
案例:
-
SELECT * FROM employees,departments; #查询到了2889条数据
-
为什么会出现以上结果:
-
SELECT COUNT(department_id) FROM departments; #一共有27条数据 SELECT COUNT(employee_id) FROM employees; #一共有107条数据 #------2889 = 107 * 27
-
原因是上述结果发生了笛卡尔积。
-
-
7.3、笛卡尔积
-
笛卡尔乘积:是一个数学运算。假设我有两个集合X和Y,那么X和Y的笛卡尔积就是X和Y的所有可能组合,也就是第一个对象来自于X,第二个对象来自于Y的所有可能。组合的个数即为两个集合中元素个数的乘积数。
-
SQL92中,笛卡尔积也称为交叉连接,英文是CROSSJOIN。在SQL99中也是使用CROSSJOIN表示交叉连接。它的作用就是可以把任意表进行连接,即使这两张表不相关。在MySQL中如下情况会出现笛卡尔积:
-
为什么会出现笛卡尔积呢:
- 因为没有限制多表查询的条件,导致每个员工和每个部门匹配了一遍
-
笛卡尔积的四种书写方式
-
SELECT last_name,department_name FROM employees,departments; #2889 SELECT last_name,department_name FROM employees CROSS JOIN departments; #2889 SELECT last_name,department_name FROM employees INNER JOIN departments; #2889 SELECT last_name,department_name FROM employees JOIN departments; #2889
-
-
笛卡尔积的错误会在下面条件下产生:
- 省略多个表的链接条件(或关联条件)
- 链接条件(或者关联条件)无效
- 所有表中的所有行互相连接
-
为了避免笛卡尔积可以加入where进行条件判断
-
加入条件后的,查询语法:
-
#----查询每个员工对应的部门名字 SELECT last_name,department_name FROM employees,departments WHERE employees.department_id = departments.department_id; #----一共106条数据-->有一个员工的department_id = null
-
在表中有相同的字段名时,要在字段名前加上表的名字来表明来自哪张表的字段
-
7.3、多表查询条件
①、多表查询的分类
- 角度一、等值连接 和 非等连接
- 角度二、自连接 和 非自连接
- **角度三、**内连接 和 外连接
②、等值连接:
-
当查询语句中出现了多个表中都存在的字段,则必须指明此字段所在的表
-
#----查询每个员工对应的部门名字 SELECT employees.last_name,employees.first_name,departments.department_id FROM employees,departments WHERE employees.department_id = departments.department_id;
-
-
给表起别名—因为重复使用了多次表名
-
可以简化查询
-
列名前使用表名前缀可以提高查询效率
-
注意:如果我们使用了表的别名,在查询字段中,过滤条件的中就只能使用别名进行代替,并不能使用原表名,使用后会报错。
-
SELECT e.employee_id,d.department_id,d.department_name FROM employees e,departments d WHERE e.department_id = d.department_id;
-
-
阿里巴巴的规范:
- **【强制】**对于数据库中表记录的查询和变更,只要涉及多个表,都需要在列名前加表的别名(或表名)进行限定。
- 说明:对多表进行查询记录、更新记录、删除记录时,如果对操作列没有限定表的别名(或表名),并且操作列在多个表中存在时,就会抛异常。
- 正例:selectt1.namefromtable_firstast1,table_secondast2wheret1.id=t2.id;
- 反例:在某业务中,由于多表关联查询语句没有加表的别名(或表名)的限制,正常运行两年后,最近在某个表中增加一个同名字段,在预发布环境做数据库变更后,线上查询语句出现出1052异常:Column’name’infieldlistisambiguous。
-
扩展:
- 连接n个表,至少需要n-1个连接条件。—>例如三个表,需要两个连接条件
③、非等值连接
-
#----查询employees表中的工资应在job_grades表中的最高工资与最低工资之间,并按从高到低排序 SELECT e.last_name,e.first_name,e.salary,j.grade_level FROM employees e,job_grades j WHERE e.salary BETWEEN j.lowest_sal AND j.highest_sal ORDER BY e.salary;
④、自连接
-
自己和自己连接—用别名使两张相同的表代表不同的意义,然后进行查询
-
#------查询各个员工的经理编号 SELECT e1.last_name,e2.last_name,e1.manager_id,e2.employee_id FROM employees e1,employees e2 WHERE e1.manager_id = e2.employee_id; #查询出last_name为‘Chen’的员工的manager的信息。 SELECT d.* FROM employees e,departments d WHERE e.manager_id = d.manager_id AND e.last_name = 'Chen'; #查询员工id,员工姓名及其管理者的id姓名 SELECT e.employee_id,e.last_name,e1.last_name FROM employees e,employees e1 WHERE e.manager_id = e1.employee_id;
-
-
SQL92:使用(+)创建连接
在SQL92中采用(+)代表从表所在的位置。即左或右外连接中,(+)表示哪个是从表。
Oracle对SQL92支持较好,而MySQL则不支持SQL92的外连接。 -
#左外连接 SELECTlast_name,department_name FROMemployees,departments WHEREemployees.department_id=departments.department_id(+); #右外连接 SELECTlast_name,department_name FROMemployees,departments WHEREemployees.department_id(+)=departments.department_id;
⑤、内连接(inner join)
-
语法:
-
SELECT 字段列表 FROM A表 INNER JOIN B表 #inner这个关键字可以省略 ON 关联条件 WHERE 等其他子句;
-
-
语法结构:
-
使用**(join。。on)**创建连接
-
SELECT table1.columnt,able2.column,table3.column FROM table1 JOIN table2 ON table1 和 table2的连接条件 JOIN table3 ON table2 和 table3的连接条件
-
SQL99采用的这种嵌套结构非常清爽、层次性更强、可读性更强,即使再多的表进行连接也都清晰可见。如果你采用SQL92,可读性就会大打折扣。
-
语法说明:
- 可以使用ON子句指定额外的连接条件。
- 这个连接条件是与其它条件分开的。
- ON子句使语句具有更高的易读性。
- 关键字JOIN、INNERJOIN、CROSSJOIN的含义是一样的,都表示内连接
-
-
-
例子说明:
-
#---获取员工id>150的员工所在部门的名字 SELECT e.employee_id,d.department_name FROM employees e JOIN departments d ON (e.department_id = d.department_id) WHERE e.employee_id > 150 ORDER BY employee_id ASC;
2022-6.1
-
⑥、外连接
-
左外连接:
-
语法:
-
SELECT 字段列表 FROM A表 LEFT JOIN B表 ON 关联条件 WHERE 等其他子句;
-
-
语法例子:
-
#----查询员工对应的部门id及名称 SELECT e.last_name, e.department_id, d.department_name FROM employees e LEFT OUTER JOIN departments d ON (e.department_id = d.department_id) ;
-
-
-
右外连接:
-
需要注意的是,LEFTJOIN和RIGHTJOIN只存在于SQL99及以后的标准中,在SQL92中不存在,只能用(+)表示。
-
语法:
-
#实现查询结果是B SELECT 字段列表 FROM A表 RIGHT JOIN B表 ON 关联条件 WHERE 等其他子句;
-
-
语法例子
-
#----查询员工对应的部门id及名称 SELECT e.last_name, e.department_id, d.department_name FROM employees e LEFT OUTER JOIN departments d ON (e.department_id = d.department_id) ;.3.3满外连接(FULLOUTERJOIN) 满外连接的结果=左右表匹配的数据+左表没有匹配到的数据+右表没有匹配到的数据。 SQL99是支持满外连接的。使用FULLJOIN或FULLOUTERJOIN来实现。 需要注意的是,MySQL不支持FULLJOIN,但是可以用LEFTJOINUNIONRIGHTjoin代替。
-
-
-
满外连接(FULL OUTER JOIN)
- 满外连接的结果=左右表匹配的数据+左表没有匹配到的数据+右表没有匹配到的数据。
SQL99是支持满外连接的。使用FULL JOIN或FULL OUTER JOIN来实现。 - 需要注意的是,MySQL不支持FULLJOIN,但是可以用LEFTJOINUNIONRIGHTjoin代替。
- 满外连接的结果=左右表匹配的数据+左表没有匹配到的数据+右表没有匹配到的数据。
⑦、union的使用
-
**合并查询结果:**利用UNION关键字,可以给出多条SELECT语句,并将它们的结果组合成单个结果集。合并时,两个表对应的列数和数据类型必须相同,并且相互对应。各个SELECT语句之间使用UNION 或 UNION ALL关键字分隔。
-
语法格式:
-
SELECTcolumnFROMtable1 UNION[ALL] SELECTcolumnFROMtable2
-
-
UNION操作符
- union操作符返回两个查询结果集的并集,并去除重复记录
-
UNION ALL操作符
-
UNIONALL操作符返回两个查询的结果集的并集。对于两个结果集的重复部分,不去重。
-
注意:执行UNIONALL语句时所需要的资源比UNION语句少。如果明确知道合并数据后的结果数据不存在重复数据,或者不需要去除重复的数据,则尽量使用UNIONALL语句,以提高数据查询的效率。
-
例子:
-
#----举例:查询部门编号>90或邮箱包含a的员工信息 SELECT * FROM employees WHERE department_id > 90 OR email LIKE '%a%'; SELECT * FROM employees WHERE department_id > 90 UNION #返回结果集,去除重复 SELECT * FROM employees WHERE email LIKE '%a%';
-
#-----举例:查询中国用户中男性的信息以及美国用户中年男性的用户信息 SELECT id,cname FROM t_chinamale WHERE csex='男' UNION ALL #返回结果集,不去重 SELECT id,tname FROM t_usmale WHERE tGender='male';
-
⑧、7中SQL joins的使用
-
代码实现:
-
#中--->内连接 A 交 B---------------------------- SELECT e.employee_id,e.last_name,d.department_name FROM employees e JOIN departments d ON e.department_id = d.department_id; #左上--->左外连接------------------------------- SELECT e.employee_id,e.last_name,d.department_name FROM employees e LEFT JOIN departments d ON e.department_id = d.department_id; #右上--->右外连接------------------------------- SELECT e.employee_id,e.last_name,d.department_name FROM employees e RIGHT JOIN departments d ON e.department_id = d.department_id; #左中图--->A - A 交 B------------------------------ SELECT e.employee_id,e.last_name,d.department_name FROM employees e LEFT JOIN departments d ON e.department_id = d.department_id WHERE d.department_id IS NULL; #右中图--->B - A 交 B------------------------- SELECT e.employee_id,e.last_name,d.department_name FROM employees e RIGHT JOIN departments d ON e.department_id = d.department_id WHERE d.department_id IS NULL; #左下图--->A 并 B-------------------------------- SELECT e.employee_id,e.last_name,d.department_name FROM employees e LEFT JOIN departments d ON e.department_id = d.department_id UNION ALL SELECT e.employee_id,e.last_name,d.department_name FROM employees e RIGHT JOIN departments d ON e.department_id = d.department_id; #右下图--->A 并 B - A 交 B------------------------- SELECT e.employee_id,e.last_name,d.department_name FROM employees e LEFT JOIN departments d ON e.department_id = d.department_id WHERE d.department_id IS NULL UNION ALL SELECT e.employee_id,e.last_name,d.department_name FROM employees e RIGHT JOIN departments d ON e.department_id = d.department_id WHERE d.department_id IS NULL;
-
⑨、自然连接
-
SQL99语法新特性
- SQL99 在 SQL92 的基础上提供了一些特殊语法,比如 NATURAL JOIN 用来表示自然连接。我们可以把自然连接理解为 SQL92 中的等值连接。它会帮你自动查询两张连接表中 所有相同的字段 ,然后进行 等值连接 。
-
在SQL92标准中:
-
#--->将两张表所有相同的字段连接---------------- SELECT employee_id,last_name,department_name FROM employees e JOIN departments d ON e.department_id = d.department_id AND e.manager_id = d.manager_id;
-
-
在SQL99标准中:
-
SELECT e.employee_id,e.last_name,d.department_name FROM employees e NATURAL JOIN departments d
-
⑩、using连接
-
当我们进行连接的时候,SQL99还支持使用 USING 指定数据表里的 同名字段 进行等值连接。但是只能配合JOIN一起使用。
-
例子展示:
-
SELECT e.employee_id,e.last_name,d.department_name FROM employees e JOIN departments d ON e.department_id = d.department_id; SELECT e.employee_id,e.last_name,d.department_name FROM employees e JOIN departments d USING (department_id); #上面两个SQL语句的含义是一样的
-
11、本章小结
-
注意:
- 我们要控制连接表的数量。多表连接就相当于嵌套for循环一样,非常消耗资源,会让SQL查询性能下降得很严重,因此不要连接不必要的表。在许多DBMS中,也都会有最大连接表的限制。
- 【强制】超过三个表禁止join。需要join的字段,数据类型保持绝对一致;多表关联查询时,保证被关联的字段需要有索引。
- 说明:即使双表join也要注意表索引、SQL性能。
- 来源:阿里巴巴《Java开发手册》
-
表连接的约束条件可以有三种方式:WHERE,ON,USING
- WHERE:适用于所有关联查询
- ON:只能和JOIN一起使用,只能写关联条件。虽然关联条件可以并到WHERE中和其他条件一起写,但分开写可读性更好。
- USING:只能和JOIN一起使用,而且要求两个关联字段在关联表中名称一致,而且只能表示关联字段值相等
2022-6-2
8、单行函数(经常阅读)
8.1、不同DBMS函数的差异
-
我们在使用 SQL 语言的时候,不是直接和这门语言打交道,而是通过它使用不同的数据库软件,即DBMS。DBMS 之间的差异性很大,远大于同一个语言不同版本之间的差异。实际上,只有很少的函数是被 DBMS 同时支持的。比如,大多数 DBMS 使用(||)或者(+)来做拼接符,而在 MySQL 中的字符串拼接函数为concat()。大部分 DBMS 会有自己特定的函数,这就意味着采用 SQL 函数的代码可移植性是很差的,因此在使用函数的时候需要特别注意。
-
MySQL提供了丰富的内置函数,这些函数使得数据的维护与管理更加方便,能够更好地提供数据的分析与统计功能,在一定程度上提高了开发人员进行数据分析与统计的效率。
MySQL提供的内置函数从 实现的功能角度 可以分为数值函数、字符串函数、日期和时间函数、流程控制函数、加密与解密函数、获取MySQL信息函数、聚合函数等。这里,我将这些丰富的内置函数再分为两类: 单行函数 、 聚合函数(或分组函数) 。 -
两种函数:
-
单行函数:
- 操作数据对象
- 接受参数返回一个结果
- 只对一行进行变换
- 每行返回一个结果
- 可以嵌套
- 参数可以是一列或一个值
2022-6-4
8.2、数值函数
①、基本函数
-
函数 用法 ABS(X) 返回x的绝对值 SIGN(X) 返回X的符号。正数返回1,负数返回-1,0返回0 PI() 返回圆周率的值 CEIL(X),CEILING(x) 返回大于或等于某个值的最小整数 FLOOR(x) 返回小于或等于某个值的最大整数 LEAST(E1,E2…) 返回列表中的最小值 GREATEST(E1,E2…) 返回列表中的最大值 MOD(X,Y) 返回X除以Y后的余数 RAND() 返回0~1的随机值 ROUND(X) 返回一个对x的值进行四舍五入后,最接近与x的整数 ROUND(X,Y) 返回一个对x的值进行四舍五入后最接近X的值,并保留到小数点后面Y位 TRUNCATE(X,Y) 返回数字x截断y位小数的结果 SQRT(X) 返回x的平方根。当x的值为负数时,返回null -
代码详细:
-
#----返回绝对值 SELECT ABS(-9); #----9 #----返回X的符号。正数返回1,负数返回-1,0返回0 SELECT SIGN(9),SIGN(-9),SIGN(0); #----1,-1,0 #----返回圆周率的值 SELECT PI(); #----3.141593 #----返回大于或等于某个值的最小整数 SELECT CEIL(5.4),CEILING(5.4); #----6,6 #----返回小于或等于某个值的最大整数 SELECT FLOOR(5.4); #----5 #----返回列表中的最小值 SELECT LEAST(5,7,2,3,9,1); #----1 #----返回列表中的最大值 SELECT GREATEST(5,7,9,12,1,8,96); #----96 #----返回X除以Y后的余数 SELECT MOD(5,2); #----1 #----返回0~1的随机值 SELECT RAND(); #----0.3000254965 #----返回0~1的随机值,其中x的值用作种子值,相同的X值会产生相同的随机数 SELECT RAND(10); #----0.6570515219 #----返回一个对x的值进行四舍五入后,最接近与x的整数 SELECT ROUND(5.2); #----5 #-----返回一个对x的值进行四舍五入后最接近x的值,并保留到小数点后y位 SELECT ROUND(5.25645,1); #----5.3 #-----返回数字x截断y位小数的结果 SELECT TRUNCATE(5.2665,3); #----5.266 #-----返回x的平方根。当x的值为负数时,返回null SELECT SQRT(9); #----3
-
②、角度与弧度互换函数
③、三角函数
④、指数与对数
⑤、进制间的转换
8.3、字符串函数
8.4、日期和时间函数
①、获取时间,日期
②、日期与时间戳的转换
③、获取月份,星期,星期数,天数
④、日期的操作函数
⑤、时间和秒钟的转换函数
⑥、计算日期和时间的函数
⑦、日期的格式化与解析
8.5、流程控制函数
8.6、加密与解密函数
8.7、MySQL信息函数
8.8、其他函数
函数请去查阅单行函数PDF文档
9、聚合函数
9.1、聚合函数的介绍
- 什么是聚合函数
- 聚合函数作用于一组数据,并对一组数据返回一个值
- 聚合函数类型
- AVG()
- SUM()
- MAX()
- MIN()
- COUNT()
- 聚合函数的语法
9.2、AVG和SUM函数
-
可以对数值型数据使用
-
SELECT AVG(salary),SUM(salary) FROM employees WHERE last_name LIKE '%J%';
-
9.3、MAX和MIN函数
-
任何数据类型的数据都可以使用
-
SELECT MIN(salary),MAX(salary) FROM employees WHERE last_name LIKE '%J%';
-
9.4、COUNT函数
-
COUINT(*)返回表中记录总数,适用任意数据类型
-
SELECT COUNT(*) FROM employees;
-
-
COUNT(expr)返回expr不为空的记录总数
-
SELECT COUNT(commission_pct) FROM employees; #此时结果为35---说明有数据为null
-
-
**问题一、**用count(*),count(1),count(列名)谁好呢?
- 其实,对于MyISAM引擎的表是没有区别的。这种引擎内部有一计数器在维护着行数。
Innodb引擎的表用count(*),count(1)直接读行数,复杂度是O(n),因为innodb真的要去数一遍。但好于具体的count(列名)。
- 其实,对于MyISAM引擎的表是没有区别的。这种引擎内部有一计数器在维护着行数。
-
**问题二、**能不能使用count(列名)替换count()?
- 不要使用count(列名)来替代count(),count()是SQL92定义的标准统计行数的语法,跟数据库无关,跟NULL和非NULL无关。
- 说明:count(*)会统计值为NULL的行,而count(列名)不会统计此列为NULL值的行。
9.5、GROUP BY(分组函数)
①、基本使用
-
WHERE一定放在from的后面
-
在SELECT列表中所有未包含在组函数中的列都应该包含在 GROUP BY子句中
-
例子:
-
#对部门ID进行分组。并求出组内数据的平均值 SELECT department_id,AVG(salary) FROM employees GROUP BY department_id;
-
包含在group by中的字段可以不出现在select语句中
-
②、使用多个列分组
-
例子:
-
#对职业进行分组,然后计算每个职业的总工资 SELECT department_id dept_id, job_id, SUM(salary) FROM employees GROUP BY department_id, job_id;
-
③、GROUPBY中使用WITHROLLUP
-
WITH ROLLUP的介绍
-
使用 WITH ROLLUP 关键字之后,在所有查询出的分组记录之后增加一条记录,该记录计算查询出的所有记录的总和,即统计记录数量。
-
SELECT SUM(salary),AVG(salary) FROM employees WHERE department_id > 80 GROUP BY department_id WITH ROLLUP;
-
-
注意:
-
当使用ROLLUP时,不能同时使用ORDER BY子句进行结果排序,即ROLLUP和ORDER BY是互相排斥的。
-
-
9.6、HAVING(过滤分组)
①、基本使用
-
注意事项:
- 行已经被分组
- 使用了聚合函数
- 满足HAVING子句中条件的分组将呗显示
- HAVING不能单独使用,必须要跟GROUP BY一起使用
-
例子详细:
-
SELECT department_id,MAX(salary) FROM employees GROUP BY department_id HAVING MAX(salary) > 10000;
-
非法使用聚合函数 : 不能在 WHERE 子句中使用聚合函数。如下
-
SELECT department_id, AVG(salary) FROM employees WHERE AVG(salary) > 8000 GROUP BY department_id;
-
-
②、WHERE和HAVING
-
区别1:WHERE 可以直接使用表中的字段作为筛选条件,但不能使用分组中的计算函数作为筛选条件;HAVING 必须要与 GROUP BY 配合使用,可以把分组计算的函数和分组字段作为筛选条件。
-
这决定了,在需要对数据进行分组统计的时候,HAVING 可以完成 WHERE 不能完成的任务。这是因为,在查询语法结构中,WHERE 在 GROUP BY 之前,所以无法对分组结果进行筛选。HAVING 在 GROUP BY 之后,可以使用分组字段和分组中的计算函数,对分组的结果集进行筛选,这个功能是 WHERE 无法完成的。另外,WHERE排除的记录不再包括在分组中。
-
-
区别2:如果需要通过连接从关联表中获取需要的数据,WHERE 是先筛选后连接,而 HAVING 是先连接后筛选 .
-
这一点,就决定了在关联查询中,WHERE 比 HAVING 更高效。因为 WHERE 可以先筛选,用一个筛选后的较小数据集和关联表进行连接,这样占用的资源比较少,执行效率也比较高。HAVING 则需要先把结果集准备好,也就是用未被筛选的数据集进行关联,然后对这个大的数据集进行筛选,这样占用的资源就比较多,执行效率也较低。
-
-
小结:
-
优点 缺点 WHERE 先筛选数据在关联 不能使用分组中的计算函数进行筛选 HAVING 可以使用分组中的计算函数 在最后的结果集中进行筛选,执行效率较低 -
开发中的选择
- WHERE 和 HAVING 也不是互相排斥的,我们可以在一个查询里面同时使用 WHERE 和 HAVING。包含分组统计函数的条件用 HAVING,普通条件用 WHERE。这样,我们就既利用了 WHERE 条件的高效快速,又发挥了 HAVING 可以使用包含分组统计函数的查询条件的优点。当数据量特别大的时候,运行效率会有很大的差别。
-
③、SELECT的执行过程
-
#方式1: SELECT ...,....,... FROM ...,...,.... WHERE 多表的连接条件 AND 不包含组函数的过滤条件 GROUP BY ...,... HAVING 包含组函数的过滤条件 ORDER BY ... ASC/DESC LIMIT ...,... #方式2: SELECT ...,....,... FROM ... JOIN ... ON 多表的连接条件 JOIN ... ON ... WHERE 不包含组函数的过滤条件 AND/OR 不包含组函数的过滤条件 GROUP BY ...,... HAVING 包含组函数的过滤条件 ORDER BY ... ASC/DESC LIMIT ...,... ##其中----解释关键字: #(1)from:从哪些表中筛选 #(2)on:关联多表查询时,去除笛卡尔积 #(3)where:从表中筛选的条件 #(4)group by:分组依据 #(5)having:在统计结果中再次筛选 #(6)order by:排序 #(7)limit:分页
④、SELECT的执行顺序
-
你需要记住一下两个顺序
-
1、关键字的顺序是不能颠倒的:
-
SELECT ... FROM ... WHERE ... GROUP BY ... HAVING ... ORDER BY ... LIMIT...
-
-
2、SELECT 语句的执行顺序(在 MySQL 和 Oracle 中,SELECT 执行顺序基本相同):
-
FROM -> WHERE -> GROUP BY -> HAVING -> SELECT 的字段 -> DISTINCT -> ORDER BY -> LIMIT
-
-
-
书写顺序例子:
-
#筛选出--名字中含 a 且 所在部门的薪资平均值 > 10000 且对部门平均薪资做降序排序,并且只显示前两行数据 SELECT e.last_name,d.department_name,MAX(e.salary) AS z_avg FROM employees e JOIN departments d ON e.department_id = d.department_id WHERE e.last_name LIKE '%a%' GROUP BY e.department_id HAVING z_avg > 10000 ORDER BY z_avg DESC LIMIT 2;
-
在 SELECT 语句执行这些步骤的时候,每个步骤都会产生一个 虚拟表 ,然后将这个虚拟表传入下一个步骤中作为输入。需要注意的是,这些步骤隐含在 SQL 的执行过程中,对于我们来说是不可见的。
⑤、SQL语句的执行原理
2022-6-5
10、子查询
10.1、概念
- SQL 中子查询的使用大大增强了 SELECT 查询的能力,因为很多时候查询需要从结果集中获取数据,或者需要从同一个表中先计算得出一个数据结果,然后与这个数据结果(可能是某个标量,也可能是某个集合)进行比较。
10.2、需求分析与问题解决
-
#方法一-------------------------- SELECT last_name,salary FROM employees WHERE last_name = 'Abel'; #查询出Abel的工资 SELECT last_name,salary FROM employees WHERE salary > 11000 #将数据库中的员工工资与其比较 #方法二--------------------------- SELECT e2.last_name,e2.salary FROM employees e1,employees e2 #自连接的方式 WHERE e1.last_name = 'Abel' AND e2.salary > e1.salary; 方法三---------------------------- SELECT last_name,salary FROM employees WHERE salary > ( #利用子查询的方式 SELECT salary FROM employees WHERE last_name = 'Abel' );
10.3、子查询的基本使用
- 子查询的基本语法结构:
- 子查询(内查询)在主查询之前一次执行完成。
- 子查询的结果被主查询**(外查询)使用** 。
- 注意事项
- 子查询要包含在括号内
- 将子查询放在比较条件的右侧
- 单行操作符对应单行子查询,多行操作符对应多行子查询
10.4、子查询的分类
-
分类方式一:
-
我们按内查询的结果返回一条还是多条记录,将子查询分为 单行子查询 、 多行子查询
- 单行子查询
- 多行子查询
- 单行子查询
-
分类方式二:
- 我们按内查询是否被执行多次,将子查询划分为 相关(或关联)子查询 和 不相关(或非关联)子查询 。
- 子查询从数据表中查询了数据结果,如果这个数据结果只执行一次,然后这个数据结果作为主查询的条件进行执行,那么这样的子查询叫做不相关子查询。
- 同样,如果子查询需要执行多次,即采用循环的方式,先从外部查询开始,每次都传入子查询进行查询,然后再将结果反馈给外部,这种嵌套的执行方式就称为相关子查询
10.5、单行子查询
①、单行比较操作符
操作符 | 含义 |
---|---|
= | equal to |
> | greater than |
>= | greater than or equal to |
< | less than |
<= | less than or equal to |
<> | not equal to |
②、代码示例
-
**题目一:**查询工资大于149号员工工资的员工的信息
-
#查询工资大于149号员工工资的员工的信息 SELECT employee_id,salary FROM employees WHERE salary > ( SELECT salary FROM employees WHERE employee_id = 149 );
-
**题目二:**返回job_id与141号员工相同,salary比143号员工多的员工姓名,job_id和工资
-
#题目:返回job_id与141号员工相同,salary比143号员工多的员工姓名,job_id和工资 SELECT last_name,job_id,salary FROM employees WHERE salary > ( SELECT salary FROM employees WHERE employee_id = 143 ) AND job_id = ( SELECT job_id FROM employees WHERE employee_id = 141 );
-
-
**题目三:**返回公司工资最少的员工的last_name,job_id和salary
-
#返回公司工资最少的员工的last_name,job_id和salary SELECT last_name,job_id,salary FROM employees WHERE salary = ( SELECT MIN(salary) FROM employees );
-
③、HAVING的子查询
-
题目四:查询最低工资大于50号部门最低工资的部门id和其最低工资
-
#题目四:查询最低工资大于50号部门最低工资的部门id和其最低工资 SELECT department_id,MIN(salary) FROM employees GROUP BY department_id HAVING MIN(salary) > ( SELECT MIN(salary) FROM employees GROUP BY department_id HAVING department_id = 50 );
-
④、CASE中的子查询
-
CASE中的子查询:题目:显式员工的employee_id,last_name和location。其中,若员工department_id与location_id为1800的department_id相同,则location为’Canada’,其余则为’USA’
-
#显式员工的employee_id,last_name和location。其中,若员工department_id与location_id为1800的department_id相同,则location为’Canada’,其余则为’USA’ SELECT employee_id, last_name, (CASE department_id WHEN (SELECT department_id FROM departments WHERE location_id = 1800) THEN 'Canada' ELSE 'USA' END) location FROM employees;
-
⑤、空值问题
-
SELECT last_name, job_id FROM employees WHERE job_id =( SELECT job_id FROM employees WHERE last_name = 'Haas' ); #此时的结果为空
⑥、2.5 非法使用子查询
-
SELECT employee_id, last_name FROM employees WHERE salary =( SELECT MIN(salary) FROM employees GROUP BY department_id );
10.6、多行子查询
①、概念
- 也称为集合比较子查询
- 内查询返回多行
- 使用多行比较操作符
②、多行比较操作符
-
操作符 含义 IN 等于列表中的任意一个 ANY 需要和单行比较操作符一起使用,和子查询返回的某一个值比较 ALL 需要和单行比较操作符一起使用,和子查询返回的所有值比较 SOME 实际上是ANY的别名,作用相同,一般常使用ANY -
体会ANY 和 ALL 的区别
③、代码示例
-
#返回其它job_id中比job_id为‘IT_PROG’部门任一工资低的员工的员工号、姓名、job_id 以及salary SELECT employee_id,last_name,job_id,salary FROM employees WHERE salary < ANY ( SELECT salary FROM employees WHERE job_id = 'IT_PROG' ) AND job_id != 'IT_PROG';
-
#返回其它job_id中比job_id为‘IT_PROG’部门所有工资都低的员工的员工号、姓名、job_id以及salary SELECT employee_id,last_name,job_id,salary FROM employees WHERE salary < ALL ( SELECT salary FROM employees WHERE job_id = 'IT_PROG' ) AND job_id != 'IT_PROG';
-
#查询平均工资最低的部门id SELECT department_id FROM employees GROUP BY department_id HAVING AVG( salary ) <= ALL ( SELECT AVG(salary) FROM employees GROUP BY department_id );
④、空值问题
-
SELECT last_name FROM employees WHERE employee_id NOT IN ( SELECT manager_id FROM employees );
10.7、相关子查询
①、相关子查询的执行流程
-
如果子查询的执行依赖于外部查询,通常情况下都是因为子查询中的表用到了外部的表,并进行了条件关联,因此每执行一次外部查询,子查询都要重新计算一次,这样的子查询就称之为 关联子查询 。
-
相关子查询按照一行接一行的顺序执行,主查询的每一行都执行一次子查询
-
子查询中使用主查询中的列
2022-6-6
②、代码示例
-
#查询员工中工资大于本部门平均工资的员工的last_name,salary和其department_id #方法一-------------------- SELECT last_name,salary,department_id FROM employees e1 WHERE salary > ( SELECT AVG(salary) FROM employees WHERE department_id = e1.department_id ); #方法二------------------在 FROM 中使用子查询 SELECT last_name,salary,e1.department_id FROM employees e1,(SELECT department_id,AVG(salary) dept_avg_sal FROM employees GROUP BY department_id) e2 WHERE e1.`department_id` = e2.department_id AND e2.dept_avg_sal < e1.`salary`; #rom型的子查询:子查询是作为from的一部分,子查询要用()引起来,并且要给这个子查询取别名, 把它当成一张“临时的虚拟的表”来使用
③、在ORDER BY 中使用子查询
-
#查询员工的id,salary,按照department_name 排序 #多表查询----------------------------------------------------------- SELECT e.employee_id,e.salary FROM employees e,departments d WHERE e.department_id = d.department_id ORDER BY d.department_name; #会自动过滤department_name为null的字段 #子查询------------------------------------------------------------- SELECT employee_id,salary FROM employees e ORDER BY ( SELECT department_name FROM departments d WHERE e.department_id = d.department_id );
-
#题目:若employees表中employee_id与job_history表中employee_id相同的数目不小于2,输出这些相同id的员工的employee_id,last_name和其job_id SELECT e.employee_id, last_name,e.job_id FROM employees e WHERE 2 <= ( SELECT COUNT(*) FROM job_history WHERE employee_id = e.employee_id );
④、EXISTS 与 NOT EXISTS关键字
- 关联子查询通常也会和exists操作符一起使用,用来检查在子查询中是否存在满足条件的行。
- 如果在子查询中不存在满足条件的行:
- 条件返回false
- 继续在子查询中查找
- 如果在子查询中满足条件的行
- 不在子查询中继续查找
- 条件返回true
- NOT EXISTS关键字表示如果不存在某种条件,则返回TRUE,否则返回FALSE。
**题目:**查询公司管理者的employee_id,last_name,job_id,department_id信息
-
#查询公司管理者的employee_id,last_name,job_id,department_id信息 #----exists SELECT employee_id, last_name, job_id, department_id FROM employees e1 WHERE EXISTS ( SELECT * FROM employees e2 WHERE e2.manager_id =e1.employee_id ); #----自连接 SELECT DISTINCT e1.employee_id, e1.last_name, e1.job_id, e1.department_id FROM employees e1 JOIN employees e2 WHERE e1.employee_id = e2.manager_id; #----多行子查询 SELECT employee_id,last_name,job_id,department_id FROM employees WHERE employee_id IN( SELECT DISTINCT manager_id FROM employees );
**题目2:**查询departments表中,不存在于employees表中的部门的department_id和department_name
-
#查询departments表中,不存在于employees表中的部门的department_id和department_name SELECT department_id, department_name FROM departments d WHERE NOT EXISTS ( SELECT 'X' FROM employees WHERE department_id = d.department_id );
⑤、相关更新
⑥、相关删除
10.8、抛出思考
2022-6-7
11、数据的增删改查
11.1、插入数据
①、values插入数据
-
使用这种语法一次只能插入一条数据
-
情况1:为表中所有字段按默认顺序插入数据
-
#插入一条数据 INSERT INTO departments VALUES (280,'Pub',100,1700); #插入成功 #插入数据的错误方式 INSERT INTO departments VALUES (80,'Pub',100,1700); #报错信息 > 1062 - Duplicate entry '80' for key 'departments.PRIMARY' #主键有唯一性,不能重复插入 > 时间: 0s
-
-
情况2:为表中指定的字段插入数据
-
为表的指定字段插入数据,就是在INSERT语句中只向部分字段中插入值,而其他字段的值为表定义时的
默认值。 -
在 INSERT 子句中随意列出列名,但是一旦列出,VALUES中要插入的value1,…valuen需要与
column1,…columnn列一一对应。如果类型不同,将无法插入,并且MySQL会产生错误。 -
INSERT INTO departments(department_id,department_name) VALUES (290,'zzj');
-
-
情况3:插入多条数据
-
INSERT INTO departments(department_id,department_name) VALUES (300,'lmg'), (310,'xxl'), (320,'zhh');
-
使用INSERT同时插入多条记录时,MySQL会返回一些在执行单行插入时没有的额外信息,这些信息的含
义如下:- ● Records:表明插入的记录条数。
- ● Duplicates:表明插入时被忽略的记录,原因可能是这些记录包含了重复的主键值。
- ● Warnings:表明有问题的数据值,例如发生数据类型转换。
-
一个同时插入多行记录的INSERT语句等同于多个单行插入的INSERT语句,但是多行的INSERT语句
在处理过程中 效率更高 。因为MySQL执行单条INSERT语句插入多行数据比使用多条INSERT语句
快,所以在插入多条记录时最好选择使用单条INSERT语句的方式插入。
-
-
小结
- VALUES 也可以写成 VALUE ,但是VALUES是标准写法。
- 字符和日期型数据应包含在单引号中。
②、将查询的结果插入到表中
-
INSERT还可以将SELECT语句查询的结果插入到表中,此时不需要把每一条记录的值一个一个输入,只需
要使用一条INSERT语句和一条SELECT语句组成的组合语句即可快速地从一个或多个表中向一个表中插入
多行 。 -
举例子:
-
#前提是查询到的数据与插入表的结构一样 INSERT INTO employees_copy1 SELECT * FROM employees WHERE department_id = 90;
-
在 INSERT 语句中加入子查询。
-
不必书写 VALUES 子句。
-
子查询中的值列表应与 INSERT 子句中的列名对应
-
11.2,、更新数据
-
使用 UPDATE 语法对数据进行更新
-
可以一次性更新多条数据
-
如果需要回滚数据,需要保证 DML 前,进行设置:SET AUTOCOMMIT = FALSE;
-
使用 WHERE 子句指定需要更新的数据
-
#指定first_name的员工的employee_id为110 UPDATE zzj SET employee_id = 110 WHERE first_name = 'Lex'; #假如舍弃 where 则全部数据将会被更新
-
-
更新数据中的完整性错误
-
UPDATE employees SET department_id = 55 #此时第55号部门不存在 WHERE department_id = 110; #1452 - Cannot add or update a child row: a foreign key constraint fails (`atguigudb`.`employees`, CONSTRAINT `emp_dept_fk` FOREIGN KEY (`department_id`) REFERENCES `departments` (`department_id`))
-
-
11.3、删除数据
-
使用 DELETE 语句从表中删除数据
-
DELETE FROM zzj WHERE employee_id = 110; #当没有 where 时,则默认删除所有的数据量
-
-
删除时发生的完整性错误:
-
DELETE FROM departments WHERE department_id = 60; #不能删除包含在另一个表中用作外键的主键的行。
-
11.4、MySQL8新特性:计算列
-
什么叫计算列呢?简单来说就是某一列的值是通过别的列计算得来的。例如,a列值为1、b列值为2,c列
不需要手动插入,定义a+b的结果为c的值,那么c就是计算列,是通过别的列计算得来的。 -
在MySQL 8.0中,CREATE TABLE 和 ALTER TABLE 中都支持增加计算列。下面以CREATE TABLE为
例进行讲解。
-
举例:定义数据表tb1,然后定义字段id、字段a、字段b和字段c,其中字段c为计算列,用于计算a+b的
值。 首先创建测试表tb1,语句如下-
CREATETABLEtb1( id INT, a INT, b INT, c INT GENERATED ALWAYS AS (a+b) VIRTUAL );
-
插入数据
-
INSERT INTO tb1(a,b)VALUES(100,200);
-
-
查询数据表tb1中的数据
-
SELECT * FROM tb1;
-
-
更新数据中的数据
-
UPDATE tb1 SET a = 500;
-
-
11.5、综合案例
- 有时间在做
2022-6-8
12、MySQL数据类型(经常阅读)
12.1、数据类型分类
-
类型 类型举例 整型类型 TINYINT、SMALLINT、MEDIUMINT、INT(或INTEGER)、BIGINT 浮点型 FLOAT、DOUBLE 定点数类型 DECIMAL 位类型 BIT 日期时间类型 YEAR、TIME、DATE、DATETIME、TIMESTAMP 文本字符串类型 CHAR、VARCHAR、TINYTEXT、TEXT、MEDIUMTEXT、LONGTEXT 枚举类型 ENUM 集合类型 SET 二进制字符串类型 BINARY、VARBINARY、TINYBLOB、BLOB、MEDIUMBLOB、LONGBLOB JSON类型 JSON对象、JSON数组 空间数据类型 单值类型:GEOMETRY、POINT、LINESTRING、POLYGON; 集合类型:MULTIPOINT、MULTILINESTRING、MULTIPOLYGON、 GEOMETRYCOLLECTION -
常见数据类型的属性,如下:
-
MySQL 含义 NULL 数据列可包含NULL值 NOT NULL 数据不允许有NULL值 DEFAULT 默认值 PRIMARY KEY 主键 AUTO_INCREMENT 自动递增,适用于整数类型 UNSIGNED 无符号 CHARACTER SRT name 指定一个字符串
-
12.2、整数类型
①、类型介绍
整数类型一共有5种,包括 TINYINT、SMALLINT、MEDIUMINT、INT(INTEGER)和 BIGINT。
它们的区别如下表所示:
整数类型 | 字节 | 有符号取值范围 | 无符号数取值范围 |
---|---|---|---|
TINYINT | 1 | -128~127 | 0~255 |
SMALLINT | 2 | -32768~32767 | 0~65535 |
MEDIUMINT | 3 | -8388608~8388607 | 0~16777215 |
INT、INTEGER | 4 | -2147483648~2147483647 | 0~4294967295 |
BIGINT | 8 | 9223372036854775808~9223372036854775807 | 0~18446744073709551615 |
②、可选属性
-
整数类型的可选属性有三个:
-
2.1–M
-
M: 表示显示宽度,M的取值范围是(0, 255)。例如,int(5):当数据宽度小于5位的时候在数字前面需要用字符填满宽度。该项功能需要配合“ ZEROFILL ”使用,表示用“0”填满宽度,否则指定显示宽度无效。
-
如果设置了显示宽度,那么插入的数据宽度超过显示宽度限制,会不会截断或插入失败?
- 答案:不会对插入的数据有任何影响,还是按照类型的实际宽度进行保存,即 显示宽度与类型可以存储的值范围无关 。从MySQL 8.0.17开始,整数数据类型不推荐使用显示宽度属性。
-
整型数据类型可以在定义表结构时指定所需要的显示宽度,如果不指定,则系统为每一种类型指定默认
的宽度值。 -
举例子:
-
CREATE TABLE test_int1 ( x TINYINT, y SMALLINT, z MEDIUMINT, m INT, n BIGINT );
-
-
TINYINT(—tinyint—)有符号数和无符号数的取值范围分别为-128127和0255,由于负号占了一个数字位,因此TINYINT默认的显示宽度为4。同理,其他整数类型的默认显示宽度与其有符号数的最小值的宽度相同。
-
举例子:
-
CREATE TABLE test_int2( f1 INT, f2 INT(5), f3 INT(5) ZEROFILL ); DESC test_int2; INSERT INTO test_int2(f1,f2,f3) VALUES(1,123,123); INSERT INTO test_int2(f1,f2) VALUES(123456,123456); INSERT INTO test_int2(f1,f2,f3) VALUES(123456,123456,123456); SELECT * FROM test_int2;
-
-
-
2.2—UNSIGNED(—unsigned—)
-
**unsigned:**无符号类型(非负),所有的整数类型都有一个可选的属性(无符号属性),无符号整数类型的最小取值为0。所以,如果需要在MySQL数据库中保存非负整数值时,可以将整数类型设置为无符号类型。
-
int类型默认显示宽度为int(11),无符号int类型默认显示宽度为int(10)。
-
CREATE TABLE test_int3( f1 INT UNSIGNED ); desc test_int3;
-
-
-
2.3----ZEROFILL
- ZEROFILL : 0填充,(如果某列是ZEROFILL,那么MySQL会自动为当前列添加UNSIGNED属性),如果指定了ZEROFILL只是表示不够M位时,用0在左边填充,如果超过M位,只要不超过数据存储范围即可。
- 原来,在 int(M) 中,M 的值跟 int(M) 所占多少存储空间并无任何关系。 int(3)、int(4)、int(8) 在磁盘上都是占用 4 bytes 的存储空间。也就是说,**int(M),必须和UNSIGNED ZEROFILL一起使用才有意义。**如果整数值超过M位,就按照实际位数存储。只是无须再用字符 0 进行填充。
③、适用场景
- TINYINT :一般用于枚举数据,比如系统设定取值范围很小且固定的场景。
- **SMALLINT :**可以用于较小范围的统计数据,比如统计工厂的固定资产库存数量等。
- **MEDIUMINT :**用于较大整数的计算,比如车站每日的客流量等。
- **INT、INTEGER :**取值范围足够大,一般情况下不用考虑超限问题,用得最多。比如商品编号。
- **BIGINT :**只有当你处理特别巨大的整数时才会用到。比如双十一的交易量、大型门户网站点击量、证
券公司衍生产品持仓等。
2.4 如何选择?
- 在评估用哪种整数类型的时候,你需要考虑 存储空间 和 可靠性 的平衡问题:一方 面,用占用字节数少
的整数类型可以节省存储空间;另一方面,要是为了节省存储空间, 使用的整数类型取值范围太小,一
旦遇到超出取值范围的情况,就可能引起 系统错误 ,影响可靠性。 - 举个例子,商品编号采用的数据类型是 INT。原因就在于,客户门店中流通的商品种类较多,而且,每
天都有旧商品下架,新商品上架,这样不断迭代,日积月累。 - 如果使用 SMALLINT 类型,虽然占用字节数比 INT 类型的整数少,但是却不能保证数据不会超出范围
65535。相反,使用 INT,就能确保有足够大的取值范围,不用担心数据超出范围影响可靠性的问题。 - 你要注意的是,在实际工作中,系统故障产生的成本远远超过增加几个字段存储空间所产生的成本。因
此,我建议你首先确保数据不会超过取值范围,在这个前提之下,再去考虑如何节省存储空间。
12.3、浮点数类型
12.4、定点数类型
12.5、位类型(bit)
12.6、日期与时间类型
12.7、文本字符串类型
12.8、enum类型
12.9、set类型
12.10、二进制字符串类型
12.11、JSON类型
12.12、空间类型
12.13、小结
- 在定义数据类型时,如果确定是 整数 ,就用 INT ; 如果是 小数 ,一定用定点数类型
DECIMAL(M,D) ; 如果是日期与时间,就用 DATETIME 。 - 这样做的好处是,首先确保你的系统不会因为数据类型定义出错。不过,凡事都是有两面的,可靠性
好,并不意味着高效。比如,TEXT 虽然使用方便,但是效率不如 CHAR(M) 和 VARCHAR(M)。 - 关于字符串的选择,建议参考如下阿里巴巴的《Java开发手册》规范:
- 阿里巴巴《Java开发手册》之MySQL数据库:
- 任何字段如果为非负数,必须是 UNSIGNED
- 【 强制 】小数类型为 DECIMAL,禁止使用 FLOAT 和 DOUBLE。
- 说明:在存储的时候,FLOAT 和 DOUBLE 都存在精度损失的问题,很可能在比较值的时候,得
到不正确的结果。如果存储的数 - 据范围超过 DECIMAL 的范围,建议将数据拆成整数和小数并
分开存储。
- 说明:在存储的时候,FLOAT 和 DOUBLE 都存在精度损失的问题,很可能在比较值的时候,得
- 【 强制 】如果存储的字符串长度几乎相等,使用 CHAR 定长字符串类型。
- 【 强制 】VARCHAR 是可变长字符串,不预先分配存储空间,长度不要超过 5000。如果存储长度大
于此值,定义字段类型为 TEXT,独立出来一张表,用主键来对应,避免影响其它字段索引效率。
13、约束概述
13.1、为什么需要约束
- 数据完整性(Data Integrity)是指数据的精确性(Accuracy)和可靠性(Reliability)。它是防止数据库中存在不符合语义规定的数据和防止因错误信息的输入输出造成无效操作或错误信息而提出的。
- 为了保证数据的完整性,SQL规范以约束的方式对表数据进行额外的条件限制。从以下四个方面考虑:
- 实体完整性(Entity Integrity) :例如,同一个表中,不能存在两条完全相同无法区分的记录
- 域完整性(Domain Integrity) :例如:年龄范围0-120,性别范围“男/女”
- 引用完整性(Referential Integrity) :例如:员工所在部门,在部门表中要能找到这个部门
- 用户自定义完整性(User-defined Integrity) :例如:用户名唯一、密码不能为空等,本部门经理的工资不得高于本部门职工的平均工资的5倍。
13.2、剩余知识点—到以前的笔记中看
14、视图
14.1、知识点到以前的笔记中看
2022-6-12
15、存储过程和存储函数
一、存储过程(了解下)
15.1、概述
- **含义:**存储过程的英文是 Stored Procedure 。它的思想很简单,就是一组经过 预先编译 的 SQL 语句的封装。
- **执行过程:**存储过程预先存储在 MySQL 服务器上,需要执行的时候,客户端只需要向服务器端发出调用存储过程的命令,服务器端就可以把预先存储好的这一系列 SQL 语句全部执行
- 好处:
- 1、简化操作,提高了SQL语句的重用性,减少了开发程序员的压力
- 2、减少操作过程中的失误,提高效率
- 3、减少网络传输量(客户端不需要把所有的 SQL 语句通过网络发给服务器) 4、减少了 SQL 语句暴露在网上的风险,也提高了数据查询的安全性
- 和视图、函数的对比:
- 它和视图有着同样的优点,清晰、安全,还可以减少网络传输量。不过它和视图不同,视图是 虚拟表 ,通常不对底层数据表直接操作,而存储过程是程序化的 SQL,可以 直接操作底层数据表 ,相比于面向集合的操作方式,能够实现一些更复杂的数据处理。
- 一旦存储过程被创建出来,使用它就像使用函数一样简单,我们直接通过调用存储过程名即可。相较于函数,存储过程是 没有返回值 的。
15.2、分类
-
存储过程的参数类型可以是IN、out和nout。根据这点分类如下:
- 1、没有参数==(无参数无返回值)==
- 2、仅仅带in类型==(有参数无返回值)==
- 3、仅仅带out类型==(无参数有返回值)==
- 4、既带in又带out==(有参数有返回值)==
- 5、带inout==(有参数有返回值)==
-
参数详细说明:
- IN :当前参数为输入参数,也就是表示入参;存储过程只是读取这个参数的值。如果没有定义参数种类, 默认就是 IN ,表示输入参数。
- OUT :当前参数为输出参数,也就是表示出参;执行完成之后,调用这个存储过程的客户端或者应用程序就可以读取这个参数返回的值了。
- INOUT :当前参数既可以为输入参数,也可以为输出参数。
- 形参类型可以是MySQL的任意类型
-
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IYpkfS0j-1657463727869)(C:\Users\lenovo\AppData\Roaming\Typora\typora-user-images\image-20220612174413014.png)]
-
存储过程体:
-
1. BEGIN…END:BEGIN…END 中间包含了多个语句,每个语句都以(;)号为结束符。 2. DECLARE:DECLARE 用来声明变量,使用的位置在于 BEGIN…END 语句中间,而且需要在其他语句使用之前进 行变量的声明。 3. SET:赋值语句,用于对变量进行赋值。 4. SELECT… INTO:把从数据表中查询的结果存放到变量中,也就是为变量赋值。
-
-
需要设置新的结束标记:
-
DELIMITER 新的结束符
-
例子详细:
-
DELIMITER $ CREATE PROCEDURE 存储过程名(IN|OUT|INOUT 参数名 参数类型,...) [characteristics ...] BEGIN sql语句1; sql语句2; END $
-
-
15.3、创建存储过程
-
CREATE PROCEDURE 存储过程名(IN|OUT|INOUT 参数名 参数类型,...) [characteristics ...] BEGIN 存储过程体 END
15.4、代码的例子
-
#举例1:创建存储过程select_all_data(),查看 emps 表的所有数据 CREATE PROCEDURE select_all_data() BEGIN SELECT * FROM zzj; end CALL select_all_data(); #举例2:创建存储过程avg_employee_salary(),返回所有员工的平均工资 CREATE DEFINER=`root`@`localhost` PROCEDURE `avg_emp`() BEGIN SELECT AVG(salary) FROM employees; END CALL avg_emp(); #举例3:创建存储过程show_min_salary(),查看“emps”表的最低薪资值。并将最低薪资通过OUT参数“ms”输出 CREATE DEFINER=`root`@`localhost` PROCEDURE `show_min`(OUT ms DOUBLE) BEGIN SELECT MIN(salary) INTO ms FROM employees; END CALL show_min(@ms); #@用户自定义的变量 #举例4:创建存储过程show_someone_salary(),查看“emps”表的某个员工的薪资,并用IN参数empname输入员工姓名 CREATE DEFINER=`root`@`localhost` PROCEDURE `show_someone_salary2`(IN emp_name VARCHAR(20),OUT emp_salary DOUBLE) BEGIN SELECT salary INTO emp_salary FROM employees WHERE last_name = emp_name; END #举例5:创建存储过程show_mgr_name(),查询某个员工领导的姓名,并用INOUT参数“empname”输入员工姓名,输出领导的姓名。 CREATE DEFINER=`root`@`localhost` PROCEDURE `show_mgr_name`(INOUT emp_name VARCHAR(20)) BEGIN SELECT last_name INTO emp_name FROM employees WHERE employee_id = (SELECT manager_id FROM employees WHERE last_name = emp_name); #查询出领导的id,进而查出名字 END
15.5、调用存储过程
①、调用格式
-
存储过程有多种调用方法。存储过程必须使用CALL语句调用,并且存储过程和数据库相关,如果要执行其他数据库中的存储过程,需要指定数据库名称,例如CALL dbname.procname。
-
CALL 存储过程名(实参列表)
-
-
格式:
-
1、调用in模式的参数
-
CALL sp1('值');
-
-
2、调用out模式的参数
-
SET @name; CALL sp1(@name); SELECT @name;
-
-
3、调用inout模式的参数
-
SET @name; CALL sp1(@name); SELECT @name;
-
-
②、代码举例
-
举例1:
-
DELIMITER // CREATE PROCEDURE CountProc(IN sid INT,OUT num INT) BEGIN SELECT COUNT(*) INTO num FROM fruits WHERE s_id = sid; END // DELIMITER ;
-
调用存储过程:
-
mysql> CALL CountProc (101, @num); Query OK, 1 row affected (0.00 sec)
-
-
查看返回结果:
-
mysql> SELECT @num;
-
-
该存储过程返回了指定 s_id=101 的水果商提供的水果种类,返回值存储在num变量中,使用SELECT查看,返回结果为3。
-
-
举例2:
-
#创建存储过程,实现累加运算,计算 1+2+…+n 等于多少。具体的代码如下: DELIMITER // CREATE PROCEDURE `add_num`(IN n INT) BEGIN DECLARE i INT; DECLARE sum INT; SET i = 1; SET sum = 0; WHILE i <= n DO SET sum = sum + i; SET i = i +1; END WHILE; SELECT sum; END // DELIMITER ;
-
如果你用的是 Navicat 工具,那么在编写存储过程的时候,Navicat 会自动设置 DELIMITER 为其他符号,我们不需要再进行 DELIMITER 的操作。
-
直接使用 CALL add_num(50); 即可。这里我传入的参数为 50,也就是统计 1+2+…+50 的积累之和
-
③、代码调试
- 在 MySQL 中,存储过程不像普通的编程语言(比如 VC++、Java 等)那样有专门的集成开发环境。因此,你可以通过 SELECT 语句,把程序执行的中间结果查询出来,来调试一个 SQL 语句的正确性。调试成功之后,把 SELECT 语句后移到下一个 SQL 语句之后,再调试下一个 SQL 语句。这样 逐步推进 ,就可以完成对存储过程中所有操作的调试了。当然,你也可以把存储过程中的 SQL 语句复制出来,逐段单独调试
二、存储函数
前面学习了很多函数,使用这些函数可以对数据进行的各种处理操作,极大地提高用户对数据库的管理效率。MySQL支持自定义函数,定义好之后,调用方式与调用MySQL预定义的系统函数一样。
15.6、语法分析
学过的函数:LENGTH、SUBSTR、CONCAT等
-
语法格式:
-
CREATE FUNCTION 函数名(参数名 参数类型,...) RETURNS 返回值类型 [characteristics ...] BEGIN 函数体 #函数体中肯定有 RETURN 语句 END
-
-
说明:
- 1、参数列表:指定参数为IN、OUT或INOUT只对PROCEDURE是合法的,FUNCTION中总是默认为IN参数。
- 2、RETURNS type 语句表示函数返回数据的类型;RETURNS子句只能对FUNCTION做指定,对函数而言这是 强制 的。它用来指定函数的返回类型,而且函数体必须包含一个 RETURN value 语句。
- 3、characteristic 创建函数时指定的对函数的约束。取值与创建存储过程时相同,这里不再赘述。
- 4、函数体也可以用BEGIN…END来表示SQL代码的开始和结束。如果函数体只有一条语句,也可以省略BEGIN…END。
15.7、调用存储函数
-
在MySQL中,存储函数的使用方法与MySQL内部函数的使用方法是一样的。换言之,用户自己定义的存储函数与MySQL内部函数是一个性质的。区别在于,存储函数是 用户自己定义 的,而内部函数是MySQL的 开发者定义的。
-
SELECT 函数名(实参列表)
-
15.8、存储函数代码举例
-
举例1:
-
#创建存储函数,名称为email_by_name(),参数定义为空,该函数查询Abel的email,并返回,数据类型为字符串型。 CREATE DEFINER=`root`@`localhost` FUNCTION `email_retrun`() RETURNS varchar(30) CHARSET utf8mb3 READS SQL DATA DETERMINISTIC BEGIN RETURN (SELECT email FROM employees WHERE last_name = 'Abel'); END SELECT email_by_name(); #调用,Navicat不用调用 #创建存储函数,名称为email_by_id(),参数传入emp_id,该函数查询emp_id的email,并返回,数据类型为字符串型 SET GLOBAL log_bin_trust_function_creators = 1; #创建带参数的函数时0 CREATE DEFINER=`root`@`localhost` FUNCTION `email_by_id`(emp_id INT) RETURNS varchar(25) CHARSET utf8mb3 BEGIN RETURN (SELECT email FROM employees WHERE employee_id = emp_id); END
-
-
注意:
- 若在创建存储函数中报错“ you might want to use the less safe
log_bin_trust_function_creators variable ”,有两种处理方法:- 方式1:
- 加上必要的函数特性“[NOT] DETERMINISTIC”和“{CONTAINS SQL | NO SQL | READS SQL DATA |MODIFIES SQL DATA}”
- 方式2:
- mysql> SET GLOBAL log_bin_trust_function_creators = 1;
- 方式1:
- 若在创建存储函数中报错“ you might want to use the less safe
15.9、对比存储函数和存储过程
关键字 | 调用语法 | 返回值 | 应用场景 | |
---|---|---|---|---|
存储过程 | PROCEDURE | CALL 存储过程() | 理解为有0个或多个 | 一般用于更新 |
存储函数 | FUNCTION | SELECT 函数 () | 只能是一个 | 一般用于查询结果为一个值并 返回时 |
- 此外,存储函数可以放在查询语句中使用,存储过程不行。反之,存储过程的功能更加强大,包括能够执行对表的操作(比如创建表,删除表等)和事务操作,这些功能是存储函数不具备的。
2022-6-16
三、存储过程以及函数的查看,修改和删除
创建完成后,怎么知道我们创建的存储过程,存储是否创建成功了呢
MySQL存储了存储过程和函数的状态信息,用户可以使用show status语句或show create语句进行查看,也可以直接从系统的information_schema数据库中查询,这里介绍3中方法
15.10、查看
-
使用show create语句查看
-
SHOW CREATE {PROCEDURE | FUNCTION} 存储过程名或函数名 SHOW CREATE PROCEDURE select_all_data;#查看存储 过程 的基本信息 SHOW CREATE FUNCTION email_by_id ; #查看存储 函数 的基本信息
-
-
使用show status语句查看
-
SHOW FUNCTION STATUS LIKE 'email_by_id'; #符合模糊查询的基本规则 SHOW PROCEDURE STATUS LIKE 'select_all_data';
-
-
从information_schema.Routines表中查看存储过程和函数的信息
-
**说明:**如果在MySQL数据库中存在存储过程和函数名称相同的情况,最好指定ROUTINE_TYPE查询条件来
指明查询的是存储过程还是函数。 -
MySQL中存储过程和函数的信息存储在information_schema数据库下的Routines表中。可以通过查询该表
的记录来查询存储过程和函数的信息。其基本语法形式如下: -
举例:从Routines表中查询名称为CountProc的存储函数的信息,代码如下
-
SELECT * FROM information_schema.Routines WHERE ROUTINE_NAME='count_by_id' AND ROUTINE_TYPE = 'FUNCTION'; #这个位置区分大小写
-
-
15.11、修改
-
说明:
-
修改存储过程或函数,不影响存储过程或函数功能,只是修改相关特性。使用ALTER语句实现
-
其中,characteristic指定存储过程或函数的特性,其取值信息与创建存储过程、函数时的取值信息略有
不同。 -
特性说明:
- CONTAINS SQL,表示子程序包含SQL语句,但不包含读或写数据的语句。
- NO SQL ,表示子程序中不包含SQL语句。
- READS SQL DATA ,表示子程序中包含读数据的语句。
- MODIFIES SQL DATA ,表示子程序中包含写数据的语句。
- SQL SECURITY { DEFINER | INVOKER } ,指明谁有权限来执行。
- DEFINER ,表示只有定义者自己才能够执行。
- INVOKER ,表示调用者可以执行。
- COMMENT ‘string’ ,表示注释信息。
-
修改存储过程使用ALTER PROCEDURE语句,修改存储函数使用ALTER FUNCTION语句。但是,这两
个语句的结构是一样的,语句中的所有参数也是一样的。
-
-
举例子:
-
#修改存储过程CountProc的定义。将读写权限改为MODIFIES SQL DATA,并指明调用者可以执行,代码如下: ALTER PROCEDURE CountProc MODIFIES SQL DATA SQL SECURITY INVOKER ;
-
15.12、删除
-
IF EXISTS:如果程序或函数不存储,它可以防止发生错误,产生一个用SHOW WARNINGS查看的警告。
-
DROP PROCEDURE CountProc; DROP FUNCTION CountProc;
-
报错信息为:
-
四、关于存储过程的使用争议
16、变量
16.1、概念
- 在MySQL数据库的存储过程和函数中,可以使用变量来存储查询或计算的中间结果数据,或者输出最终
的结果数据。 - 在 MySQL 数据库中,变量分为 系统变量 以及 用户自定义变量 。
16.2、系统变量
①、系统变量的分类
- 变量由系统定义,不是用户定义,属于 服务器 层面。启动MySQL服务,生成MySQL服务实例期间,MySQL将为MySQL服务器内存中的系统变量赋值,这些系统变量定义了当前MySQL服务实例的属性、特征。这些系统变量的值要么是 编译MySQL时参数 的默认值,要么是 配置文件 (例如my.ini等)中的参数值。大家可以通过网址 https://dev.mysql.com/doc/refman/8.0/en/server-systemvariables.html 查看MySQL文档的系统变量。
- 系统变量分为全局系统变量(需要添加 global 关键字)以及会话系统变量(需要添加 session 关键字),有时也把全局系统变量简称为全局变量,有时也把会话系统变量称为local变量。如果不写,默认会话级别。静态变量(在 MySQL 服务实例运行期间它们的值不能使用 set 动态修改)属于特殊的全局系统变量。
- 每一个MySQL客户机成功连接MySQL服务器后,都会产生与之对应的会话。会话期间,MySQL服务实例会在MySQL服务器内存中生成与该会话对应的会话系统变量,这些会话系统变量的初始值是全局系统变量值的复制。如下图:
-
- 全局系统变量针对于所有会话(连接)有效,但 不能跨重启
- 会话系统变量仅针对于当前会话(连接)有效。会话期间,当前会话对某个会话系统变量值的修改,不会影响其他会话同一个会话系统变量的值。
- 会话1对某个全局系统变量值的修改会导致会话2中同一个全局系统变量值的修改。
②、查看系统变量
-
查看所有或部分系统变量:
-
#查看所有全局变量 SHOW GLOBAL VARIABLES; #查看所有会话变量 SHOW SESSION VARIABLES; 或 SHOW VARIABLES; #默认会话级别的变量 #查看满足条件的部分系统变量。 SHOW GLOBAL VARIABLES LIKE 'admin_%'; #查看满足条件的部分会话变量 SHOW SESSION VARIABLES LIKE 'admin_%';
-
查看指定系统变量:
-
作为 MySQL 编码规范,MySQL 中的系统变量以 两个“@” 开头,其中“@@global”仅用于标记全局系统变
量,“@@session”仅用于标记会话系统变量。“@@”首先标记会话系统变量,如果会话系统变量不存在,则标记全局系统变量。-
#查看指定的系统变量的值 SELECT @@global.变量名; SELECT @@global.character_set_client; #utf8mb4 #查看指定的会话变量的值 SELECT @@session.变量名; SELECT @@session.pseudo_thread_id; #8 #或者 SELECT @@变量名; #现在会话中找,最后再去系统变量里找
-
③、修改系统变量的值
-
有些时候,数据库管理员需要修改系统变量的默认值,以便修改当前会话或者MySQL服务实例的属性、
特征。具体方法:- 方式1:修改MySQL 配置文件 ,继而修改MySQL系统变量的值(该方法需要重启MySQL服务)
- 方式2:在MySQL服务运行期间,使用“set”命令重新设置系统变量的值
-
举例:
-
#为某个系统变量赋值 #方式1: SET @@global.变量名=变量值; #方式2: SET GLOBAL 变量名=变量值; #为某个会话变量赋值 #方式1: SET @@session.变量名=变量值; #方式2: SET SESSION 变量名=变量值;
-
16.3、用户变量
①、用户变量的分类
用户变量是用户自己定义的,作为MySQL编码规范,MySQL中的用户变量以==一个’@'==开头。根据作用范围不同,有分为 会话用户变量 和 局部变量。
- 会话用户变量:作用域和会话变量一样,只对 当前链接 会话有效
- 局部变量:只在BEGIN 和 END 语句快中有效。局部变量只能在存储过程与函数中使用
②。会话用户变量
-
变量的定义
-
#方式1:“=”或“:=” SET @用户变量 = 值; SET @用户变量 := 值; #方式2:“:=” 或 INTO关键字 SELECT @用户变量 := 表达式 [FROM 等子句]; SELECT 表达式 INTO @用户变量 [FROM 等子句];
-
-
查看用户变量的值 (查看、比较、运算等)
-
SELECT @用户变量
-
-
举例子:
-
SET @a = 1; SELECT @a; SELECT @num := COUNT(*) FROM employees; SELECT @num; SELECT AVG(salary) INTO @avgsalary FROM employees; SELECT @avgsalary; SELECT @big; #查看某个未声明的变量时,将得到NULL值
-
③、局部变量
- 定义:可以使用 DECLARE 语句定义一个局部变量
- 作用域:仅仅在定义它的 BEGIN … END 中有效
- 位置:只能放在 BEGIN … END 中,而且只能放在第一句
-
定义变量
-
DECLARE myparam INT DEFAULT 100;
-
-
给变量赋值
-
#方式一 SET enp_name = 'Abel'; #方式二 SELECT last_name,salary INTO emp_name,sal FROM employees; #给变量赋值
-
-
使用变量
-
SELECT emp_name;
-
-
综合例子
-
#声明局部变量,并分别赋值为employees表中employee_id为102的last_name和salary DELIMITER // CREATE PROCEDURE set_value() BEGIN DECLARE emp_name VARCHAR(25); #定义变量 DECLARE sal DOUBLE(10,2); SELECT last_name,salary INTO emp_name,sal #给变量赋值 FROM employees WHERE employee_id = 102; SELECT emp_name,sal; END // DELIMITER ;
-
#声明两个变量,求和并打印 (分别使用会话用户变量、局部变量的方式实现) #方式1:使用用户变量----------------------- SET @m=1; SET @n=1; SET @sum=@m+@n; SELECT @sum; #方式2:使用局部变量---------------------- DELIMITER // CREATE PROCEDURE add_value() BEGIN #局部变量 DECLARE m INT DEFAULT 1; DECLARE n INT DEFAULT 3; DECLARE SUM INT; SET SUM = m+n; SELECT SUM; END // DELIMITER ;
-
④、对比会话用户变量与局部变量
17、定义条件与程序处理
定义条件 是事先定义程序执行过程中可能遇到的问题, 处理程序 定义了在遇到问题时应当采取的处理方式,并且保证存储过程或函数在遇到警告或错误时能继续执行。这样可以增强存储程序处理问题的能力,避免程序异常停止运行。
**说明:**定义条件和处理程序在存储过程、存储函数中都是支持的。
17.1、错误案例分析
-
INSERT INTO employees(last_name) VALUES('TOM'); #1364 - Field 'email' doesn't have a default value #email字段必须进行赋值----因为它不允许为空
-
DELIMITER // CREATE PROCEDURE UpdateDataNoCondition() BEGIN SET @x = 1; UPDATE employees SET email = NULL WHERE last_name = 'Abel'; SET @x = 2; UPDATE employees SET email = 'aabbel' WHERE last_name = 'Abel'; SET @x = 3; END // DELIMITER ; #报错信息 1048 - Column 'email' cannot be null
17.2、定义条件
定义条件就是给MySQL中的错误码命名,这有助于存储的程序代码更清晰。它将一个 错误名字 和 指定的错误条件 关联起来。这个名字可以随后被用在定义处理程序的 DECLARE HANDLER 语句中。
-
定义条件使用DECLARE语句,语法格式如下 :
-
DECLARE 错误名称 CONDITION FOR 错误码(或错误条件)
-
-
错误码的说明:
- MySQL_error_code 和 sqlstate_value 都可以表示MySQL的错误。
- MySQL_error_code是数值类型错误代码。
- sqlstate_value是长度为5的字符串类型错误代码。
- 例如,在ERROR 1418 (HY000)中,1418是MySQL_error_code,'HY000’是sqlstate_value。
- 例如,在ERROR 1142(42000)中,1142是MySQL_error_code,'42000’是sqlstate_value。
-
举例子
-
#定义“Field_Not_Be_NULL”错误名与MySQL中违反非空约束的错误类型是“ERROR 1048 (23000)”对应 #使用MySQL_error_code DECLARE Field_Not_Be_NULL CONDITION FOR 1048; #使用sqlstate_value DECLARE Field_Not_Be_NULL CONDITION FOR SQLSTATE '23000'; #怕起冲突所以使用SQLstate区分开来
-
17.3、定义错误处理语句
-
可以为SQL执行过程中发生的某种类型的错误定义特殊的处理程序。定义处理程序时,使用DECLARE语句
的语法如下:-
DECLARE 处理方式 HANDLER FOR 错误类型 处理语句
-
-
处理方式:处理方式有3个取值:CONTINUE、EXIT、UNDO。
- CONTINUE :表示遇到错误不处理,继续执行。
- EXIT :表示遇到错误马上退出。
- UNDO :表示遇到错误后撤回之前的操作。MySQL中暂时不支持这样的操作。
-
错误类型(即条件)可以有如下取值:
-
SQLSTATE ‘字符串错误码’ :表示长度为5的sqlstate_value类型的错误代码;
-
MySQL_error_code :匹配数值类型错误代码;
-
错误名称 :表示DECLARE … CONDITION定义的错误条件名称。
-
SQLWARNING :匹配所有以01开头的SQLSTATE错误代码;
-
NOT FOUND :匹配所有以02开头的SQLSTATE错误代码;
-
SQLEXCEPTION :匹配所有没有被SQLWARNING或NOT FOUND捕获的SQLSTATE错误代码;
-
-
**处理语句:**如果出现上述条件之一,则采用对应的处理方式,并执行指定的处理语句。语句可以是像“ SET 变量 = 值 ”这样的简单语句,也可以是使用 BEGIN … END 编写的复合语句。
-
举例子:
-
#方法1:捕获sqlstate_value DECLARE CONTINUE HANDLER FOR SQLSTATE '42S02' SET @info = 'NO_SUCH_TABLE'; #方法2:捕获mysql_error_value DECLARE CONTINUE HANDLER FOR 1146 SET @info = 'NO_SUCH_TABLE'; #方法3:先定义条件,再调用 DECLARE no_such_table CONDITION FOR 1146; DECLARE CONTINUE HANDLER FOR NO_SUCH_TABLE SET @info = 'NO_SUCH_TABLE'; #方法4:使用SQLWARNING DECLARE EXIT HANDLER FOR SQLWARNING SET @info = 'ERROR'; #方法5:使用NOT FOUND DECLARE EXIT HANDLER FOR NOT FOUND SET @info = 'NO_SUCH_TABLE'; #方法6:使用SQLEXCEPTION DECLARE EXIT HANDLER FOR SQLEXCEPTION SET @info = 'ERROR';
-
17.4、案例解决
-
**例题一、**在存储过程中,定义处理程序,捕获sqlstate_value值,当遇到MySQL_error_code值为1048时,执行
CONTINUE操作,并且将@proc_value的值设置为-1。-
CREATE DEFINER=`root`@`localhost` PROCEDURE `UpdateDataNoCondition`() BEGIN DECLARE CONTINUE HANDLER FOR 1048 SET @proc_value = -1; #程序不报错,被处理,程序继续往下运行 SET @x = 1; UPDATE employees SET email = NULL WHERE last_name = 'Abel'; SET @x = 2; UPDATE employees SET email = 'aabbel' WHERE last_name = 'Abel'; SET @x = 3; SELECT @proc_value; #这里的值是-1 SELECT @X; #这里的值是3 END
-
18、流程控制(经常阅读)
18.1、分支结构–IF
18.2、分支结构–CASE
18.3、循环结构–Loop
18.4、循环结构–WHILE
18.5、循环结构–REPEAT
18.6、跳转语句–LEAVE
18.7、跳转语句–ITERATE
19、游标
19.1、什么是游标(光标)
- 虽然我们也可以通过筛选条件 WHERE 和 HAVING,或者是限定返回记录的关键字 LIMIT 返回一条记录,
但是,却无法在结果集中像指针一样,向前定位一条记录、向后定位一条记录,或者是 随意定位到某一
条记录 ,并对记录的数据进行处理。 - 这个时候,就可以用到游标。游标,提供了一种灵活的操作方式,让我们能够对结果集中的每一条记录
进行定位,并对指向的记录中的数据进行操作的数据结构。游标让 SQL 这种面向集合的语言有了面向过
程开发的能力。 - 在 SQL 中,游标是一种临时的数据库对象,可以指向存储在数据库表中的数据行指针。这里游标 充当了
指针的作用 ,我们可以通过操作游标来对数据行进行操作
19.2、使用游标的步骤
游标必须在声明处理程序之前被声明,并且变量和条件还必须在声明游标或处理程序之前被声明。
如果我们想要使用游标,一般需要经历四个步骤。不同的 DBMS 中,使用游标的语法可能略有不同。
-
第一步:声明游标
-
DECLARE cursor_name CURSOR FOR select_statement
-
这个语法适用于 MySQL,SQL Server,DB2 和 MariaDB。如果是用 Oracle 或者 PostgreSQL,需要写成:
-
DECLARE cursor_name CURSOR IS select_statement;
-
-
要使用 SELECT 语句来获取数据结果集,而此时还没有开始遍历数据,这里 select_statement 代表的是
SELECT 语句,返回一个用于创建游标的结果集。-
DECLARE cur_emp CURSOR FOR SELECT employee_id,salary FROM employees; #--------------------------------------------- DECLARE cursor_fruit CURSOR FOR SELECT f_name, f_price FROM fruits ;
-
-
-
第二步:打开游标
-
打开游标的语法如下:
-
OPEN cursor_name
-
-
当我们定义好游标之后,如果想要使用游标,必须先打开游标。打开游标的时候 SELECT 语句的查询结果集就会送到游标工作区,为后面游标的 逐条读取 结果集中的记录做准备
-
OPEN cur_emp ;
-
-
-
第三歩:使用游标
-
这句的作用是使用 cursor_name 这个游标来读取当前行,并且将数据保存到 var_name 这个变量中,游
标指针指到下一行。如果游标读取的数据行有多个列名,则在 INTO 关键字后面赋值给多个变量名即可。 -
FETCH cur_emp INTO emp_id, emp_sal ; #但是--emp_id, emp_sal是在游标声明前声明好的
- 注意:游标的查询结果集中的字段数,必须跟 INTO 后面的变量数一致,否则,在存储过程执行的时
候,MySQL 会提示错误。
- 注意:游标的查询结果集中的字段数,必须跟 INTO 后面的变量数一致,否则,在存储过程执行的时
-
-
第四步:关闭游标
-
CLOSE cursor_name
-
有 **OPEN 就会有 CLOSE,**也就是打开和关闭游标。当我们使用完游标后需要关闭掉该游标。因为游标会
占用系统资源 ,如果不及时关闭,游标会一直保持到存储过程结束,影响系统运行的效率。而关闭游标
的操作,会释放游标占用的系统资源。 -
关闭游标之后,我们就不能再检索查询结果中的数据行,如果需要检索只能再次打开游标。
-
19.3、案例
-
#创建存储过程“get_count_by_limit_total_salary()”,声明IN参数 limit_total_salary,DOUBLE类型;声明OUT参数total_count,INT类型。函数的功能可以实现累加薪资最高的几个员工的薪资值,直到薪资总和达到limit_total_salary参数的值,返回累加的人数给total_count。 DELIMITER // CREATE PROCEDURE get_count_by_limit_total_salary(IN limit_total_salary DOUBLE,OUT total_count INT) BEGIN DECLARE sum_salary DOUBLE DEFAULT 0; #记录累加的总工资 DECLARE cursor_salary DOUBLE DEFAULT 0; #记录某一个工资值 DECLARE emp_count INT DEFAULT 0; #记录循环个数 #定义游标 DECLARE emp_cursor CURSOR FOR SELECT salary FROM employees ORDER BY salary DESC; #打开游标 OPEN emp_cursor; REPEAT #使用游标(从游标中获取数据) FETCH emp_cursor INTO cursor_salary; SET sum_salary = sum_salary + cursor_salary; SET emp_count = emp_count + 1; UNTIL sum_salary >= limit_total_salary END REPEAT; SET total_count = emp_count; #关闭游标 CLOSE emp_cursor; END // DELIMITER ;
19.4、小结
20、触发器
**1、**在实际开发中,我们经常会遇到这样的情况:有 2 个或者多个相互关联的表,如 商品信息 和 库存信息 分别存放在 2 个不同的数据表中,我们在添加一条新商品记录的时候,为了保证数据的完整性,必须同时在库存表中添加一条库存记录。
**2、**这样一来,我们就必须把这两个关联的操作步骤写到程序里面,而且要用 事务 包裹起来,确保这两个操作成为一个 原子操作 ,要么全部执行,要么全部不执行。要是遇到特殊情况,可能还需要对数据进行手动维护,这样就很 容易忘记其中的一步 ,导致数据缺失。
**3、**这个时候,咱们可以使用触发器。你可以创建一个触发器,让商品信息数据的插入操作自动触发库存数
据的插入操作。这样一来,就不用担心因为忘记添加库存数据而导致的数据缺失了。
20.1、概念
- MySQL从 5.0.2 版本开始支持触发器。MySQL的触发器和存储过程一样,都是嵌入到MySQL服务器的一段程序。
- 触发器是由 事件来触发 某个操作,这些事件包括 INSERT 、 UPDATE 、 DELETE 事件。所谓事件就是指用户的动作或者触发某项行为。如果定义了触发程序,当数据库执行这些语句时候,就相当于事件发生了,就会 自动 激发触发器执行相应的操作。
- 当对数据表中的数据执行插入、更新和删除操作,需要自动执行一些数据库逻辑时,可以使用触发器来实现
20.2、触发器的创建
-
语法结构
-
CREATE TRIGGER 触发器名称 {BEFORE|AFTER} {INSERT|UPDATE|DELETE} ON 表名 FOR EACH ROW 触发器执行的语句块;
-
-
语法结构说明
- **表名 :**表示触发器监控的对象。
- **BEFORE|AFTER :**表示触发的时间。BEFORE 表示在事件之前触发;AFTER 表示在事件之后触发。
- **INSERT|UPDATE|DELETE :**表示触发的事件。
- INSERT 表示插入记录时触发;
- UPDATE 表示更新记录时触发;
- DELETE 表示删除记录时触发。
- 触发器执行的语句块 :可以是单条SQL语句,也可以是由BEGIN…END结构组成的复合语句块
20.3、创建触发器语法
-
#创建触发器:创建名称为before_insert的触发器,向test_trigger数据表插入数据之前,向test_trigger_log数据表中插入before_insert的日志信息。 CREATE TRIGGER before_insert_test_tri BEFORE INSERT ON test_trigger FOR EACH ROW BEGIN INSERT INTO test_trigger_log (t_log) VALUES('before_insert_test_tri'); END #开始向test_trigger表中插入数据----- INSERT INTO test_trigger(id) VALUES(12);
-
#创建名称为after_insert的触发器,向test_trigger数据表插入数据之后,向test_trigger_log数据表中插入after_insert的日志信息。 CREATE TRIGGER after_insert_test_tri AFTER INSERT ON test_trigger FOR EACH ROW #---立即执行 BEGIN INSERT INTO test_trigger_log (t_log) VALUES('after_insert_test_tri'); END #开始向test_trigger表中插入数据 INSERT INTO test_trigger(id) VALUES(13);
-
#定义触发器“salary_check_trigger”,基于员工表“employees”的INSERT事件,在INSERT之前检查将要添加的新员工薪资是否大于他领导的薪资,如果大于领导薪资,则报sqlstate_value为'HY000'的错误,从而使得添加失败。 CREATE TRIGGER salary_check_trigger_emp BEFORE INSERT ON employees FOR EACH ROW BEGIN DECLARE mar_salary DOUBLE; SELECT salary INTO mar_salary FROM employees WHERE employee_id = NEW.manager_id; IF mar_salary < NEW.salary THEN SIGNAL SQLSTATE 'HY000' SET MESSAGE_TEXT = '薪资高于领导薪资错误'; END IF; END
20.4、查看触发器
查看触发器是查看数据库中已经存在的触发器的定义、状态和语法信息等。
-
**方式一:**查看当前数据库的所有触发器的定义
-
SHOW TRIGGER \G;
-
-
**方式二:**查看当前数据库中某个触发器的定义
-
SHOW CREATE TRIGGER 触发器名;
-
-
**方式三:**从系统库information_schema的TRIGGERS表中查询“salary_check_trigger”触发器的信息。
-
SELECT * FROM information_schema.TRIGGERS;
-
20.5、删除触发器
-
触发器也是数据库对象,删除触发器也用DROP语句,语法格式如下:
-
DROP TRIGGER IF EXISTS 触发器名;
-
20.6、触发器的优缺点、注意点
BY salary DESC;
#打开游标
OPEN emp_cursor;
REPEAT
#使用游标(从游标中获取数据)
FETCH emp_cursor INTO cursor_salary;
SET sum_salary = sum_salary + cursor_salary;
SET emp_count = emp_count + 1;
UNTIL sum_salary >= limit_total_salary
END REPEAT;
SET total_count = emp_count;
#关闭游标
CLOSE emp_cursor;
END //
DELIMITER ;
#### 19.4、小结
[外链图片转存中...(img-jMqPBEnq-1657463727872)]
## 20、触发器
>**1、**在实际开发中,我们经常会遇到这样的情况:有 2 个或者多个相互关联的表,如 ==商品信息== 和 ==库存信息== 分别存放在 2 个不同的数据表中,我们在添加一条新商品记录的时候,为了保证数据的完整性,必须同时在库存表中添加一条库存记录。
>**2、**这样一来,我们就必须把这两个关联的操作步骤写到程序里面,而且要用 事务 包裹起来,确保这两个操作成为一个 原子操作 ,要么全部执行,要么全部不执行。要是遇到特殊情况,可能还需要对数据进行手动维护,这样就很 容易忘记其中的一步 ,导致数据缺失。
>**3、**这个时候,咱们可以使用触发器。你可以创建一个触发器,让商品信息数据的插入操作自动触发库存数
>据的插入操作。这样一来,就不用担心因为忘记添加库存数据而导致的数据缺失了。
#### 20.1、概念
- MySQL从 ==5.0.2== 版本开始支持触发器。MySQL的触发器和存储过程一样,都是嵌入到MySQL服务器的一段程序。
- 触发器是由 ==事件来触发== 某个操作,这些事件包括 ==INSERT 、 UPDATE 、 DELETE== 事件。所谓事件就是指用户的动作或者触发某项行为。如果定义了触发程序,当数据库执行这些语句时候,就相当于事件发生了,就会 ==自动== 激发触发器执行相应的操作。
- 当对数据表中的数据执行插入、更新和删除操作,需要自动执行一些数据库逻辑时,可以使用触发器来实现
#### 20.2、触发器的创建
- 语法结构
- ```sql
CREATE TRIGGER 触发器名称
{BEFORE|AFTER} {INSERT|UPDATE|DELETE} ON 表名
FOR EACH ROW
触发器执行的语句块;
```
- 语法结构说明
- **表名 :**表示触发器监控的对象。
- **BEFORE|AFTER :**表示触发的时间。BEFORE 表示在事件之前触发;AFTER 表示在事件之后触发。
- **INSERT|UPDATE|DELETE :**表示触发的事件。
- **INSERT** 表示插入记录时触发;
- **UPDATE** 表示更新记录时触发;
- **DELETE** 表示删除记录时触发。
- **触发器执行的语句块** :可以是单条SQL语句,也可以是由BEGIN…END结构组成的复合语句块
#### 20.3、创建触发器语法
- ```sql
#创建触发器:创建名称为before_insert的触发器,向test_trigger数据表插入数据之前,向test_trigger_log数据表中插入before_insert的日志信息。
CREATE TRIGGER before_insert_test_tri
BEFORE INSERT ON test_trigger
FOR EACH ROW
BEGIN
INSERT INTO test_trigger_log (t_log)
VALUES('before_insert_test_tri');
END
#开始向test_trigger表中插入数据-----
INSERT INTO test_trigger(id)
VALUES(12);
-
[外链图片转存中…(img-CKxnRVFy-1657463727872)]
-
[外链图片转存中…(img-xNS84STS-1657463727873)]
-
#创建名称为after_insert的触发器,向test_trigger数据表插入数据之后,向test_trigger_log数据表中插入after_insert的日志信息。 CREATE TRIGGER after_insert_test_tri AFTER INSERT ON test_trigger FOR EACH ROW #---立即执行 BEGIN INSERT INTO test_trigger_log (t_log) VALUES('after_insert_test_tri'); END #开始向test_trigger表中插入数据 INSERT INTO test_trigger(id) VALUES(13);
-
#定义触发器“salary_check_trigger”,基于员工表“employees”的INSERT事件,在INSERT之前检查将要添加的新员工薪资是否大于他领导的薪资,如果大于领导薪资,则报sqlstate_value为'HY000'的错误,从而使得添加失败。 CREATE TRIGGER salary_check_trigger_emp BEFORE INSERT ON employees FOR EACH ROW BEGIN DECLARE mar_salary DOUBLE; SELECT salary INTO mar_salary FROM employees WHERE employee_id = NEW.manager_id; IF mar_salary < NEW.salary THEN SIGNAL SQLSTATE 'HY000' SET MESSAGE_TEXT = '薪资高于领导薪资错误'; END IF; END
20.4、查看触发器
查看触发器是查看数据库中已经存在的触发器的定义、状态和语法信息等。
-
**方式一:**查看当前数据库的所有触发器的定义
-
SHOW TRIGGER \G;
-
-
**方式二:**查看当前数据库中某个触发器的定义
-
SHOW CREATE TRIGGER 触发器名;
-
-
**方式三:**从系统库information_schema的TRIGGERS表中查询“salary_check_trigger”触发器的信息。
-
SELECT * FROM information_schema.TRIGGERS;
-
20.5、删除触发器
-
触发器也是数据库对象,删除触发器也用DROP语句,语法格式如下:
-
DROP TRIGGER IF EXISTS 触发器名;
-
20.6、触发器的优缺点、注意点
[外链图片转存中…(img-F6qq9I7r-1657463727873)]