四、MySQL 高级(DQL 查)

四、MySQL 高级(DQL 查)

4.1 DQL 数据查询语言

Data Query Language,用于查询数据库的表中数据,是数据库中最为核心的语言,使用频率最高


4.2 基础查询

4.2.1 查询全部行和列

SELECT * FROM 表名称
SELECT * FROM patient
# 查询 patient 病人表中的所有字段 * 表示所有字段

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JjUiypof-1680502580614)(./assets/image-20230402180217861.png)]


4.2.2 查询部分列

SELECT 字段1,字段2,字段n FROM 表名称
SELECT patientID,patientName,gender FROM patient
# 查询 patient 病人表中的部分字段(多个字段之间使用,分隔)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UMvUJ5ff-1680502580617)(./assets/image-20230402180548009.png)]

有的时候我们不想让用户看到所有的信息,比如用户密码这类的隐私信息,我们就可以通过写具体的字段,就只查询部分信息

在开发中,推荐使用以查询部分列的方式代替SELECT *查询全部列,可提高执行效率并养成良好编码习惯

4.2.3 AS 别名

  • 使用 AS 关键字,可以为表、字段、查询结果指定别名
SELECT 字段列表 FROM 表名 AS 表的别名

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eAFZanNq-1680502580617)(./assets/image-20230402185049461.png)]

SELECT 字段1 AS 别名,字段2 FROM 表名

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1ZnhTHvP-1680502580617)(./assets/image-20230402185113942.png)]

  • 使用空格,简便的方法
SELECT 字段列表 FROM 表名  表的别名;
# 中间是空格

为某个表命名了别名后,在 SELECT 语句中出现该表的字段需要指定表名时,就必须统一使用该表的别名;否则将产生语法错误


4.3 运算符

运算符是一种符号,用来进行列间或变量之间的比较和数学运算

MySQL 常的运算符

  • 算术运算符
  • 比较运算符
  • 逻辑运算符
  • 位运算符

4.3.1 算术运算符

运算符 作用
+ 加法
- 减法
* 乘法
/ 或 DIV 除法
% 或 MOD 取余

在除法运算和模运算中,如果除数为0,将是非法除数,返回结果为 NULL

4.3.2 比较运算符

符号 描述 备注
= 等于
<>, != 不等于
> 大于
< 小于
<= 小于等于
>= 大于等于
BETWEEN 在两值之间
NOT BETWEEN 不在两值之间
IN 在集合中
NOT IN 不在集合中
<=> 严格比较两个NULL值是否相等 两个操作码均为NULL时,其所得值为1;而当一个操作码为NULL时,其所得值为0
LIKE 模糊匹配
REGEXP 或 RLIKE 正则式匹配
IS NULL 为空
IS NOT NULL 不为空

4.3.3 逻辑运算符

运算符号 作用
NOT 或 ! 逻辑非
AND 逻辑与
OR 逻辑或
XOR 逻辑异或

逻辑运算符用来判断表达式的真假。如果表达式是真,结果返回 1。如果表达式是假,结果返回 0

4.3.4 位运算符

运算符号 作用
& 按位与
| 按位或
^ 按位异或
! 取反
<< 左移
>> 右移

位运算符是在二进制数上进行计算的运算符。位运算会先将操作数变成二进制数,进行位运算。然后再将计算结果从二进制数变回十进制数

4.3.5 运算符优先级

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HCbQP5oy-1680502580618)(./assets/1011652-20170416163043227-1936139924.png)]


4.4 条件查

如果我们查询时,并不想显示所有的用户信息,只要查出满足指定条件的部分用户,或者精确到某一个用户的信息,就需要使用到 WHERE 关键字对 sql 语句增加查询条件

4.4.1 精确查询

SELECT * FROM patient WHERE patient.patientID = 1
# 通过患者 ID 精确查询

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XMiGzgAa-1680502580618)(./assets/image-20230402181442241.png)]

这里我们就把患者 ID 作为查询条件,查询出患者 ID 为 1 的患者信息

4.4.2 单条件查询

SELECT * FROM patient WHERE patient.patientID < 10
# 查询病人 ID 小于 10 的患者信息

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BdHWpCxV-1680502580618)(./assets/image-20230402182833603.png)]

SELECT * FROM patient WHERE patientName ='玄子'
# 查询所有病人姓名为玄子的患者信息

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PSGaQyMy-1680502580619)(./assets/image-20230402224936557.png)]

4.4.3 多条件查询

  • 如果查询条件中包含的条件不止一个,则条件根据逻辑关系的不同可以分为条件和条件两种

  • 条件表示要求同时满足两个以上的条件,使用 AND 关键字可以构造条件。

  • 条件表示几个条件中只需满足其中一个的条件,使用 OR 关键字可以构造条件

  • AND

SELECT * FROM patient WHERE patientName ='玄子' AND address='长春市'
# 多条件查询 姓名为 玄子 且住址为 长春市

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XzsP9Q2Z-1680502580619)(./assets/image-20230402230857608.png)]

  • OR
SELECT * FROM patient WHERE patientName ='玄子' OR address='长春市'
# 多条件查询 姓名为 玄子 '或者' 住址为 长春市

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-P5vDB9Bh-1680502580619)(./assets/image-20230402231059106.png)]

姓名 和 住址 这两个条件满足一个即可


4.5 排序查

4.5.1 升序排序

SELECT * FROM patient ORDER BY birthDate
# 通过病人的出生年月进行排序

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ebkqs3C5-1680502580620)(./assets/image-20230402183208506.png)]

这里我们使用 ORDER BY 关键字,对病人的出生年月进行排序,默认是从小到大,也就是升序排列

4.5.2 降序排序

SELECT * FROM patient ORDER BY birthDate DESC
# 通过病人的出生年月进行降序排序

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Rr9UxEd3-1680502580620)(./assets/image-20230402183508864.png)]

使用降序排列只需要 在最后加上一个DESC关键字即可

升序排序后面其实也有一个关键字ASC不过ORDER BY默认就是升序排列,所以ASC加不加都可以

4.5.3 多字段排序

SELECT * FROM patient ORDER BY birthDate ASC,patientID DESC
# 通过病人的出生年月和ID进行多字段排序

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-p1Hnmco6-1680502580620)(./assets/image-20230402183948955.png)]

如果想要对多个字段进行排序,可直接使用,分隔。然后写第二个需要排序的字段和排序方式即可


4.6 常用函数

MySQL 中将一些常用的数据处理操作封装起来,作为函数提供给程序员使用,可以提高程序员开发效率

MySQ L支持的常用函数

  • 字符串函数

  • 时间日期函数

  • 聚合函数

  • 数学函数

4.6.1 字符串函数

函数名 作用 举例
CONCAT(str1,str2,strn) 连接字符串str1、str2、strn为一个完整字符串 SELECT CONCAT( ‘MySQL’,’ is powerful.'); 返回:MySQL is powerful.
LOWER(str) 将字符串str中所有字符变为小写 SELECT LOWER( ‘MySQL is powerful.’); 返回:mysql is powerful.
UPPER(str) 将字符串str中所有字符变为大写 SELECT UPPER( ‘MySQL is powerful.’); 返回:MYSQL IS POWERFUL.
SUBSTRING(str,num,len) 返回字符串str的第num个位置开始长度为len的子字符串 SELECT SUBSTRING( ‘MySQL is powerful.’,10,8); 返回:powerful
INSERT(str,pos,len,newstr) 将字符串str从pos位置开始,len个字符长的子串替换为字符串newstr SELECT INSERT( ‘MySQL is powerful.’,10,0,'very '); 返回:MySQL is very powerful.

4.6.2 时间日期函数

函数名 作用 举例(部分结果与当前日期有关)
CURDATE() 获取当前日期 SELECT CURDATE(); 返回:2020-08-03
CURTIME() 获取当前时间 SELECT CURTIME(); 返回:16:54:40
NOW() 获取当前日期和时间 SELECT NOW(); 返回:2020-08-03 16:55:00
WEEK(date) 返回日期date为一年中的第几周 SELECT WEEK(NOW()); 返回:31
YEAR(date) 返回日期date的年份 SELECT YEAR(NOW()); 返回:2020
HOUR(time) 返回时间time的小时值 SELECT HOUR(NOW()); 返回:16
MINUTE(time) 返回时间time的分钟值 SELECT MINUTE(NOW()); 返回:56
DATEDIFF(date1,date2) 返回日期参数date1和date2之间相隔的天数 SELECT DATEDIFF(NOW(), ‘2019-8-8’); 返回:361
ADDDATE(date,n) 计算日期参数date加上n天后的日期 SELECT ADDDATE(NOW(), 5); 返回:2020-08-07 16:57:28
UNIX_TIMESTAMP(date) 将日期转换成时间戳 SELECT UNIX_TIMESTAMP(‘2020-9-1’); 返回:1598889600
DATE_ADD(d,INTERVAL expr type) 计算起始日期 d 加上一个时间段后的日期 SELECT DATE_ADD(‘2017-06-15’, INTERVAL 10 DAY); 返回:2017-06-25

DATE_ADD(d,INTERVAL expr type) type 参数可以是下列值:MICROSECOND,SECOND,MINUTE,HOUR,DAY,WEEK,MONTH,QUARTER,YEAR,SECOND_MICROSECOND,MINUTE_MICROSECOND,MINUTE_SECOND,HOUR_MICROSECOND,HOUR_SECOND,HOUR_MINUTE,DAY_MICROSECOND,DAY_SECOND,DAY_MINUTE,DAY_HOUR,YEAR_MONTH

4.6.3 聚合函数

函数名 作用
COUNT() 返回某字段的行数
MAX() 返回某字段的最大值
MIN() 返回某字段的最小值
SUM() 返回某字段的和
AVG() 返回某字段的平均值

4.6.4 数学函数

函数名 作用 举例
CEIL(x) 返回大于或等于数值x的最小整数 SELECT CEIL(-2.1); 返回:32
FLOOR(x) 返回小于或等于数值x的最大整数 SELECT FLOOR(-2.1); 返回:-3
RAND() 返回0~1间的随机数 SELECT RAND(); 返回:0.15013303621684485

MySQL 中还有许多专业领域或不常用的函数,想具体了解见文章附件:第八章 8.4 MySQL 函数


4.7 分组查

4.7 .1 GROUP BY

如果我们想要对,查询结果分组,比如按照,考试的科目ID 进行分组。就要使用GROUP BY 关键字对 subjectNo进行分组,首先我们可以看到成绩表中有四个字段分别是studentNo 学生学号subjectNo 课程编号examDate 考试日期studentResult 考试成绩。其中subjectNo有三个编号我们就可以对其分组查询查出这三个编号

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LqJitzDm-1680502580621)(./assets/image-20230402190256908.png)]

  • 分组考试成绩表中的科目编号
SELECT subjectNo FROM result GROUP BY subjectNo
# 分组考试成绩表中的科目编号

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-N4AVB7tT-1680502580621)(./assets/image-20230402190524327.png)]

这样分组就只能看到成绩表中的科目 ID,并不能查询其他字段,所以我们通常搭配聚合函数使用

  • 分组科目编号,查询对应科目考试成绩的平均分
SELECT subjectNo,AVG(studentResult) FROM result GROUP BY subjectNo
# 分组科目编号,查询对应科目考试成绩的平均分

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oH1RLfj8-1680502580621)(./assets/image-20230402231713206.png)]

3.3.5 HAVING

我们在使用 GROUP BY 分组后还想要进一步筛选查询结果,就需要使用 HAVING 关键字进一步筛选,这里并不能使用 WHERE

  • 分组考试成绩表中的科目编号,后再次筛选
SELECT subjectNo,AVG(studentResult) AS avg FROM result GROUP BY subjectNo HAVING avg >60
# 分组科目编号,查询对应科目考试成绩的平均分 后继续筛选 成绩在 60 分以上的 科目编号

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uglZmU7w-1680502580621)(./assets/image-20230402231957986.png)]


4.8 分页查

SELECT  <字段名列表>
FROM  <表名或视图>
[WHERE  <查询条件>]
[GROUP BY <分组的字段名>] [HAVING <条件>]
[ORDER BY <排序的字段名> [ASC 或 DESC]]
[LIMIT [位置偏移量,]行数];

4.8.1 下标 5 开始查询 5 条

SELECT * FROM patient LIMIT 5,5
# 位置偏移量:第1条记录的位置偏移量是0,第2条记录的位置偏移量是1……
# 行数:显示记录的条数

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4cIne1kH-1680502580622)(./assets/image-20230402233340898.png)]

使用LIMIT子句时,第1条记录的位置是0!

LIMIT 子句经常和 ORDER BY 子句一起使用,即先对查询结果进行排序,再根据 LIMIT 子句的参数返回指定的数据

4.8.2 每页 m 个数据查第 n 页

SELECT * FROM patient LIMIT (n-1)*m,m
# 每页 m 个第 n 页

4.9 模糊查

4.9.1 通配符

模糊查询一般使用关键字 LIKE 主要用于匹配列中的数据

通配符 解释 示例
_ 一个字符 A LIKE ‘V_’,则符合条件的A如“VR”、“VC”等
% 任意长度的字符串 B LIKE ‘SQL%’,则符合条件的A如“SQL Server”、“SQL Server高级编程”等
[] 括号中所指定范围内的一个字符 C LIKE ‘900[1-2]’,则符合条件的C如“9001”或“9002”
[^] 不在括号中所指定范围内的任意一个字符 D LIKE ‘900[^1-2]’,则符合条件的D如“9003”或“9007”等

4.9.2 LIKE

select * from Students where StudentName like '张%'
# 名字以张开头的人
select * from Students where StudentName like '%张'
# 名字以张结尾的人
select * from Students where StudentName like '%张%'
# 名字中包含张的人
select * from Students where StudentName like '张_'
# 名字为张姓的单名人
select * from Students where StudentName like '张[1-4]'
# 查询结果为张姓的张1到张4
select * from Students where StudentName like '张[^1-4]'
# 查询结果为张姓的除了张1到张4的所有张姓单名

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VW6cXuNi-1680502580622)(./assets/image-20230402234207243.png)]

  • 匹配的字符串必须加单引号或者双引号

  • 默认情况下,LIKE 关键字匹配字符串时候不区分大小写,可以在 LIKE 关键字后添加 BINARY 关键字来区分大小写。

  • 如果查询内容中有通配符字符,就需要加转义字符 \

4.9.3 BETWEEN

SELECT 字段 FROM 表名 WHERE 列名 [NOT] BETWEEN 起始值 AND 最终值
SELECT * FROM patient WHERE patient.patientID BETWEEN 5 AND 15
# 查询 ID 在 5 到 15 的患者信息

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xVigEVZS-1680502580622)(./assets/image-20230402235352933.png)]

NOT 可选参数,表示取反

查询指定范围内所有值,包括起始值和最终值

4.9.4 IS NULL

IS NULL关键字判断该列的值是否为空值,空值不是空字符串

SELECT 字段 FROM 表名 WHERE 列名 IS [NOT] NULL
SELECT * FROM patient WHERE address IS NOT NULL

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Wbdv53sv-1680502580622)(./assets/image-20230402235909456.png)]

NOT 可选参数,表示取反


4.10 子查询

子查询是一个嵌套在 SELECT、INSERT、UPDATE 或 DELETE 语句或其他子查询中的查询

SELECT …… FROM 表1 WHERE 字段1 比较运算符 (子查询);
SELECT * FROM patient WHERE patientID IN(1,2,3,4,5,9)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-drEI5jCV-1680502580623)(./assets/image-20230403000309493.png)]

SELECT * FROM patient WHERE patientID IN(SELECT patientID FROM patient WHERE patient.patientID BETWEEN 5 AND 15)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TEpX4G4k-1680502580623)(./assets/image-20230403000418635.png)]

先执行子查询,返回所有来自子查询的结果

再执行外围的父查询,返回查询的最终结果

将子查询和比较运算符联合使用

必须保证子查询返回的值不能多于一个

4.10.1 IN 子查询

  • 如果子查询的结果为多个值,就会导致代码报错
  • 解决方案就是使用 IN 关键字,将 = 替换成 IN
SELECT …… FROM 表名 WHERE 字段名  IN (子查询);

4.10.2 NOT IN 子查询

  • in 一样,查询结果有多条使用

  • 获取的值是不包含在查询结果里面的值

SELECT …… FROM 表名 WHERE 字段名 NOT IN (子查询);

4.10.3 EXISTS

DROP TABLE IF EXISTS temp;
CREATE TABLE log (
    … … #省略建表语句
) ;

4.10.4 EXISTS 子查询

  • 子查询有返回结果: EXISTS 子查询结果为 TRUE
  • 子查询无返回结果: EXISTS 子查询结果为 FALSE,外层查询不执行
SELECT …… FROM 表名 WHERE  EXISTS (子查询);

4.10.5 NOT EXIST 子查询

  • 与 EXISTS 子查询相反
  • 子查询有返回结果: NOT EXIST子查询结果为 FALSE
  • 子查询无返回结果: NOT EXIST子查询结果为 TRUE,外层查询执行

4.10.6 子查询小结

当一个查询是另一个查询的条件时,称之为子查询

  • 任何允许使用表达式的地方都可以使用子查询

  • 嵌套在父查询SELECT语句的子查询,可包括

    • SELECT 子句

    • SELECT (子查询) [AS 列别名] FROM 表名;
      
    • FROM 子句

    • SELECT * FROM (子查询)  AS 表别名;
      
    • WHERE 子句

    • GROUP BY 子句

    • HAVING 子句

4.10.7 子查询注意事项

通常,将子查询放在比较条件的右边以增加可读性

子查询可以返回单行或多行数据,此时要选择合适的关键字

  • 子查询的返回是单行数据时,比较条件中可以使用比较运算符

  • 子查询的返回是多行数据时,比较条件中需要使用IN或 NOT IN 关键字

  • 如果判断子查询是否有数据返回时,需要使用 EXISTS 或 NOT EXISTS 关键字

只出现在子查询中、而没有出现在父查询中的列不能包含在输出列中


4.11 连接查

4.11.1 JOIN

JOIN 按照功能大致分为如下三类

  • INNER JOIN(内连接,或等值连接):获取两个表中字段匹配关系的记录
  • **LEFT JOIN(左连接):**获取左表所有记录,即使右表没有对应匹配的记录
  • RIGHT JOIN(右连接): 与 LEFT JOIN 相反,用于获取右表所有记录,即使左表没有对应匹配的记录

4.11.2 内连接

最典型、最常用的连接查询,根据两张表中共同的列进行匹配,两个表存在主外键关系时,通常会使用内连接查询

SELECT 	……  FROM 表1 INNER JOIN 	表2 ON 	……

INNER JOIN用来连接两个表

INNER可以省略

ON用来设置两个表之间的关联条件

SELECT * FROM department_checkitem 
INNER JOIN department ON department_checkitem.depID=department.depID
INNER JOIN checkitem ON department_checkitem.checkItemID=checkitem.checkItemID

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9qptBwjF-1680502580623)(./assets/image-20230403001247130.png)]

4.11.3 外连接

特点

  • 外连接可以分为主表和从表
  • 主表的数据会被完全显示出来
  • 从表中只显示满足连接条件的数据
  • 外连接结果一般会比主表和从表中数据量最小的表中的数据多

  • 左外连
SELECT * FROM prescription
LEFT JOIN patient ON prescription.patientID=patient.patientID

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZTVDxLVp-1680502580624)(./assets/image-20230403001525010.png)]

  • 右外连
SELECT * FROM prescription
RIGHT JOIN patient ON prescription.patientID=patient.patientID

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9r659cBL-1680502580624)(./assets/image-20230403001641747.png)]


五、企业级开发技术

5.1 存储过程

关于存储过程我只能说请看下图,这是阿里巴巴发布的《阿里巴巴Java开发手册(终极版)v1.3版本》在 MySQL 第七条中强制指出禁止使用存储过程

所以对于存储过程不必深究,做到会写能看懂即可

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-36sAcKTC-1680502580624)(./assets/%E6%90%9C%E7%8B%97%E6%88%AA%E5%9B%BE20230329084802.png)]

5.1.1 什么是存储过程

Stored Procedure

  • 是一组为了完成特定功能的 SQL 语句集合
  • 经编译后保存在数据库中
  • 通过指定存储过程的名字并给出参数的值
  • MySQL5.0 版本开始支持存储过程,使数据库引擎更加灵活和强大

5.1.2 存储过程可以包含

  • 可带参数,也可返回结果
  • 可包含数据操纵语句、变量、逻辑控制语句等

5.1.3 存储过程的优缺点

优点

  • 减少网络流量
  • 提升执行速度
  • 减少数据库连接次数
  • 安全性高
  • 复用性高

缺点

  • 可移植性差

SQL 最大的缺点还是 SQL 语言本身的局限性 SQL 本身是一种结构化查询语言,我们不应该用存储过程处理复杂的业务逻辑让 SQL 回归它结构化查询语言的功用。复杂的业务逻辑,还是交给代码去处理吧

5.1.4 创建存储过程

CREATE
    [DEFINER = { user | CURRENT_USER }]  
    # 定义DEFINER默认为当前用户
PROCEDURE 存储过程名
    [SQL SECURITY { DEFINER | INVOKER } | …]
    # 指定DEFINER或INVOKER权限
BEGIN
    …
END
特性 说明
LANGUAGE SQL 表示存储过程语言,默认SQL
{CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA} 表示存储过程要做的工作类别默认值为CONTAINS SQL
SQL SECURITY { DEFINER | INVOKER } 指定存储过程的执行权限默认值是DEFINERDEFINDER:使用创建者的权限INVOKER:用执行者的权限
COMMENT ‘string’ 存储过程的注释信息

如果省略 SQL SECURITY 特性,则使用 DEFINER 属性指定调用者,且调用者必须具有 EXECUTE 权限,必须在 mysql.user 表中如果将 SQL SECURITY 特性指定为 INVOKER,则 DEFINER 属性无效

5.1.5 定义存储过程的参数

IN:指输入参数

  • 该参数的值必须在调用存储过程时指定
  • 存储过程中可以使用该参数,但它不能被返回

OUT:指输出参数

  • 该参数可以在存储过程中发生改变,并可以返回

INOUT:指输入输出参数

  • 该参数的值在调用存储过程时指定
  • 在存储过程中可以被改变和返回

如果需要定义多个参数,需要使用,进行分隔

5.1.6 调用存储过程

CALL 存储过程名([参数1,参数2, …]);
# 根据存储过程的定义包含相应的参数

存储过程调用类似于Java中的方法调用

5.1.7 查看存储过程状态

SHOW PROCEDURE STATUS

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qm2luvDd-1680502580624)(./assets/image-20230403133422488.png)]

5.1.8 查看存储创建代码

SHOW CREATE PROCEDURE 存储过程名

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RfEqJMLf-1680502580625)(./assets/image-20230403133612810.png)]

5.1.9 修改存储过程

ALTER PROCEDURE 存储过程名[特性………]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-P7x2HRAG-1680502580625)(./assets/image-20230403133809483.png)]

5.1.10 删除存储过程

DROP PROCEDURE 存储过程名

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MOKUmqzh-1680502580625)(./assets/image-20230403133956314.png)]

5.1.11 存储过程中的变量

与Java语言类似,定义存储过程时可以使用变量

DECLARE 变量名[,变量名...] 数据类型 [DEFAULT 值];

给变量进行赋值

SET 变量名 = 表达式值[,变量名=表达式...] ;

定义存储过程时,所有局部变量的声明一定要放在存储过程体的开始;否则,会提示语法错误

系统变量

  • 指 MySQL 全局变量,以@@开头,形式为@@变量名

用户自定义变量

  • 局部变量
    • 一般用于SQL的语句块中,如:存储过程中的BEGIN和END语句块
    • 作用域仅限于定义该变量的语句块内
    • 生命周期也仅限于该存储过程的调用期间
    • 在存储过程执行到END时,局部变量就会被释放
  • 会话变量
    • 是服务器为每个客户端连接维护的变量,与MySQL客户端是绑定的
    • 也称作用户变量
    • 可以暂存值,并传递给同一连接中其他SQL语句进行使用
    • 当MySQL客户端连接退出时,用户变量就会被释放
    • 用户变量创建时,一般以@开头,形式为@变量名

演示案例

  • 根据病人名称和检查项目ID输出最后一次检查时间
CREATE DEFINER=`root`@`localhost` PROCEDURE `proc_exam_GetLastExamDateByPatientNameAndDepID`(IN patient_name VARCHAR(50), IN dep_id INT,OUT last_exam_date DATETIME)
BEGIN
	#Routine body goes here...
  DECLARE patient_id INT;  #声明局部变量
  SELECT patientID INTO patient_id FROM patient WHERE patientName= patient_name;
  SELECT patient_id; #输出病人的ID
  SELECT MAX(examDate) INTO last_exam_date FROM prescription WHERE patientID = patient_id AND depID = dep_id;
END
  • 调用存储过程
SET  @patient_name='夏颖';
SET  @dep_id =1;
CALL proc_exam_GetLastExamDateByPatientNameAndDepID(@patient_name, @dep_id, @last);

SELECT @last;

5.1.12 存储过程控制语句

与Java语言的流程控制语句类似,MySQL提供的控制语句

  • 条件语句
    • IF-ELSE IF-ELSE 条件语句
    • CASE 条件语句
  • 循环语句
    • WHILE 循环
    • LOOP 循环
    • REPEAT循环
  • 迭代语句

5.1.13 IF-ELSE 条件语句

IF 条件 THEN 语句列表
   [ELSEIF 条件 THEN 语句列表]
   [ELSE 语句列表]
END IF;

根据病人的家庭收入,返还补贴不同比例的医疗费用

  • 家庭年收入在5000元以下的返还当年总医疗费用的20%
  • 家庭年收入在10000以下的返还当年总医疗费用的15%
  • 家庭年收入在30000以下的返还总医疗费用的5%
  • 30000元以上或未登记的不享受医疗费用返还
  • 输入病人编号和年份,计算该患者当年的应返还的医疗费用
CREATE DEFINER=`root`@`localhost` PROCEDURE `Proc_CH05_4`(IN patient_ID INT ,IN in_year VARCHAR(50),OUT ou_subsidy FLOAT  )
BEGIN
 DECLARE tital_Cost FLOAT;
 DECLARE totial_income FLOAT;
 
SELECT incomeMoney INTO totial_income FROM income WHERE patientID =patient_ID;

SELECT sum(checkItemCost) INTO tital_Cost FROM prescription  
INNER JOIN checkitem ON prescription.checkItemID=checkitem.checkItemID 
WHERE patientID=patient_ID AND examDate >= CONCAT(in_year,'-01-01') 
AND examDate <= CONCAT(in_year,'-12-31');


IF totial_income>=0 AND totial_income<5000 THEN
	SET ou_subsidy =tital_Cost*0.2;
ELSEIF totial_income>=5000 AND totial_income<10000 THEN
	SET ou_subsidy =tital_Cost*0.15;
ELSEIF totial_income>=10000 AND totial_income<30000 THEN
	SET ou_subsidy =tital_Cost*0.05;
ELSE
	SET ou_subsidy =0;
END IF;
END

5.1.14 CASE 条件语句

CASE
   WHEN 条件 THEN 语句列表
   [WHEN 条件 THEN 语句列表]
   [ELSE 语句列表]
END CASE;
CASE 列名
   WHEN 条件值 THEN 语句列表
   [WHEN 条件值 THEN 语句列表]
   [ELSE 语句列表]
END CASE;

使用CASE语句实现返还补贴不同比例的医疗费用

CREATE DEFINER=`root`@`localhost` PROCEDURE `Proc_CH05_5`(IN patient_ID INT ,IN in_year VARCHAR(50),OUT ou_subsidy FLOAT  )
BEGIN
 DECLARE tital_Cost FLOAT;
 DECLARE totial_income FLOAT;
 
SELECT incomeMoney INTO totial_income FROM income WHERE patientID =patient_ID;


SELECT sum(checkItemCost) INTO tital_Cost FROM prescription  
INNER JOIN checkitem ON prescription.checkItemID=checkitem.checkItemID 
WHERE patientID=patient_ID AND examDate >= CONCAT(in_year,'-01-01') 
AND examDate <= CONCAT(in_year,'-12-31');


CASE 
	WHEN totial_income>=0 AND totial_income<5000 THEN
		SET ou_subsidy =tital_Cost*0.2;
	WHEN totial_income>=5000 AND totial_income<10000 THEN
		SET ou_subsidy =tital_Cost*0.15;
	WHEN totial_income>=10000 AND totial_income<30000 THEN
		SET ou_subsidy =tital_Cost*0.05;
	WHEN totial_income>=30000 AND totial_income<0 THEN
		SET ou_subsidy =0;
END CASE;

END

在某种情况下(例如,做等值判断),使用第二种写法更加简洁但是,因为CASE后面有列名,功能上会有一些限制


5.1.15 WHILE 循环语句

[label:] WHILE 条件 DO
   语句列表
END WHILE [label]
  • 首先判断条件是否成立。如果成立,则执行循环体
  • label为标号,用于区分不同的循环,可省略
  • 用在begin、repeat、while 或者loop 语句前

假设有测试表test,有Id字段、Val字段

  • 根据输入的行数要求,批量插入测试数据
DECLARE rand_val FLOAT;
WHILE rows > 0 DO
  SELECT RAND() INTO rand_val;
  INSERT INTO test VALUES(NULL, rand_val);
  SET rows = rows - 1;
END WHILE;

5.1.16 LOOP 循环语句

[label:] LOOP
   语句列表
END LOOP [label] ;

不需判断初始条件,直接执行循环体

LEAVE label ;

遇到 LEAVE 语句,退出循环

批量插3个新的检查项目,检查项目名称为胃镜、肠镜和支气管纤维镜,各项检查的价格均为70元

CREATE DEFINER=`root`@`localhost` PROCEDURE `proc_checkitem_insert`( IN checkitems VARCHAR(100))
BEGIN
 	DECLARE comma_pos INT;
  DECLARE current_checkitem VARCHAR(20);
	loop_label: LOOP
		SET comma_pos = LOCATE(',', checkitems);
		SET current_checkitem = SUBSTR(checkitems, 1, comma_pos-1);
		IF current_checkitem <> '' THEN
			SET checkitems = SUBSTR(checkitems, comma_pos+1);
    ELSE
      SET current_checkitem = checkitems;
    END IF;
    INSERT INTO checkitem(checkItemName,checkItemCost) VALUES(current_checkitem,70);
		IF comma_pos=0 OR current_checkitem='' THEN
      LEAVE loop_label;
      # 退出loop_label标识的程序块
    END IF;
	END LOOP loop_label;
	# LOOP循环结束
END

5.1.17 REPEAT 循环语句

[label:] REPEAT
   语句列表
UNTIL 条件
END REPEAT [label]
  • 先执行循环操作再判断循环条件
  • 与 LOOP 循环语句相比较相同点
  • 不需要初始条件直接进入循环体
  • 不同点:REPEAT 语句可以设置退出条件

使用REPEAT循环语句编码实现,根据输入的行数要求,向测试表test中批量插入测试数据

CREATE DEFINER=`root`@`localhost` PROCEDURE `Proc_CH05_7`(IN rows INT )
BEGIN
  
	DECLARE rand FLOAT;
	
	REPEAT
	SELECT RAND() INTO rand;
		INSERT INTO test (val)VALUES(rand);
		SET rows = rows -1 ;
  UNTIL rows <= 0 END REPEAT;

END

5.1.18 迭代语句

ITERATE label;
  • 从当前代码处返回到程序块开始位置,重新执行
  • ITERATE关键字可以嵌入到LOOP、WHILE和REPEAT程序块中

输入需增加数据行数,随机产生的测试数据必须大于0.5

CREATE DEFINER=`root`@`localhost` PROCEDURE `Proc_CH05_8`(IN rows INT)
BEGIN 
	DECLARE rand FLOAT; 
	random_lbl : REPEAT
		SELECT RAND() INTO rand; 
			IF rand< 0.5 THEN 
				ITERATE random_lbl; 
			END IF;
		INSERT INTO test (val) VALUES (rand);
		SET rows=rows-1; 
	UNTIL rows<=0 END REPEAT; 
END

你可能感兴趣的:(MySQL,mysql,数据库,sql)