数据库学习笔记(十六)--控住流程与游标

前言:

学习和使用数据库可以说是程序员必须具备能力,这里将更新关于MYSQL的使用讲解,大概应该会更新30篇+,涵盖入门、进阶、高级(一些原理分析);
这一篇和上一篇差不多,当做扩展,用到的时候再查即可(毕竟数据库最多的还是增删改查 )。这一篇很像我们常用的高级语言的基础课程。
虽然MYSQL命令很多,但是自己去多敲一点,到后面忘记了,查一下就可以回忆起来使用了;
这一系列也是本人学习MYSQL做的笔记,也是为了方便后面忘记查询;
参考资料:黑马、csdn和知乎博客;
欢迎收藏 + 关注,本人将会持续更新后端和AI算法的学习笔记。
文章目录
1. 流程控制
1.1 分支结构之 IF
1.2 分支结构之 CASE
1.3 循环结构之 LOOP
1.4 循环结构之 WHILE
1.5 循环结构之 REPEAT
1.6 跳转语句之 LEAVE语句
1.7 跳转语句之 ITERATE 语句
2. 游标
2.1 什么是游标(或光标)
4.2 使用游标步骤
2.3 举例
2.4 小结
1. 流程控制
解决复杂问题不可能通过一个 SQL 语句完成,我们需要执行多个 SQL 操作。流程控制语句的作用就是控制存储过程中 SQL 语句的执行顺序,是我们完成复杂操作必不可少的一部分。只要是执行的程序,流程就分为三大类:

顺序结构 :程序从上往下依次执行
分支结构 :程序按条件进行选择执行,从两条或多条路径中选择一条执行
循环结构 :程序满足一定条件下,重复执行一组语句
针对于MySQL 的流程控制语句主要有 3 类。注意:只能用于存储程序。

条件判断语句 :IF 语句和 CASE 语句
循环语句 :LOOP、WHILE 和 REPEAT 语句
跳转语句 :ITERATE (iterate)和 LEAVE (leave)语句
1.1 分支结构之 IF
IF 语句的语法结构是:

IF 表达式1 
    THEN 操作1
[ELSEIF 表达式2 THEN 操作2] 
...
[ELSE 操作N]     #注意:else没有then
END IF
AI写代码
mysql
1
2
3
4
5
6
根据表达式的结果为TRUE或FALSE执行相应的语句。这里“[]”中的内容是可选的。

特点:① 不同的表达式对应不同的操作
​ ②使用在begin end中

范例1:
IF val IS NULL
THEN SELECT 'val is null';
ELSE SELECT 'val is not null';      #注意:else没有then
END IF
AI写代码
mysql
1
2
3
4
**范例2:**声明存储过程“update_sal_by_eno”,定义IN参数eno,输入员工编号。判断该员工 薪资如果低于2000元并且入职时间超过5年,就涨薪500元;否则就不变。

CREATE PROCEDURE update_sal_by_eno1(IN eno INT) 
BEGIN
    DECLARE emp_sal DOUBLE; 
    DECLARE hire_year DOUBLE;

    SELECT sal INTO emp_sal FROM emps WHERE empno = eno;

    SELECT DATEDIFF(CURDATE(),hiredate)/365 INTO hire_year 
    FROM emps WHERE empno = eno;

    IF emp_sal < 2000 AND hire_year > 5
    THEN UPDATE emps SET sal = sal + 500 WHERE empno = eno; 
    END IF;
END ;
AI写代码
mysql

1
2
3
4
5
6
7
8
9
10
11
12
13
14
**范例3:**声明存储过程“update_sal_by_eno2”,定义IN参数eno,输入员工编号。判断该员工 薪资如果低于3000元,就更新薪资为3000元;薪资如果大于等于3000元且低于5000的,但是奖金 比例为NULL的,就更新奖金比例为0.01;其他的涨薪100元。

DELIMITER //
CREATE PROCEDURE update_sal_by_eno2(IN eno INT) 
BEGIN
    DECLARE emp_sal DOUBLE; 
    DECLARE emp_comm DECIMAL(5,2);

    SELECT sal INTO emp_sal FROM emps WHERE empno = eno; 
    SELECT comm INTO emp_comm FROM emps WHERE empno = eno;

    IF emp_sal < 3000
        THEN UPDATE emps SET sal = 3000 WHERE empno = eno;
        SELECT '1';
    ELSEIF emp_sal < 5000 AND emp_comm IS NULL
        THEN UPDATE emps SET comm = 0.01*sal WHERE empno = eno;
        SELECT '2';
    ELSE   #注意:else没有then
        UPDATE emps SET sal = sal + 100 WHERE empno = eno; 
        SELECT '3';
    END IF;
END //
DELIMITER //
AI写代码
mysql

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
1.2 分支结构之 CASE
CASE 语句的语法结构1:

#情况一:类似于switch 
CASE 表达式
WHEN 值1 THEN 结果1或语句1(如果是语句,需要加分号) 
WHEN 值2 THEN 结果2或语句2(如果是语句,需要加分号)
...
ELSE 结果n或语句n(如果是语句,需要加分号)
END [case](如果是放在begin end中需要加上case,如果放在select后面不需要)
AI写代码
mysql
1
2
3
4
5
6
7
CASE 语句的语法结构2:

#情况二:类似于多重if 
CASE
WHEN 条件1 THEN 结果1或语句1(如果是语句,需要加分号)
WHEN 条件2 THEN 结果2或语句2(如果是语句,需要加分号)
...
ELSE 结果n或语句n(如果是语句,需要加分号)
END [case](如果是放在begin end中需要加上case,如果放在select后面不需要)
AI写代码
mysql
1
2
3
4
5
6
7
**范例1:**使用CASE流程控制语句的第1种格式,判断val值等于1、等于2,或者两者都不等。

CASE val
    WHEN 1 THEN SELECT 'val is 1'; 
    WHEN 2 THEN SELECT 'val is 2';
    ELSE SELECT 'val is not 1 or 2'; 
END CASE;
AI写代码
mysql
1
2
3
4
5
**范例2:**使用CASE流程控制语句的第2种格式,判断val是否为空、小于0、大于0或者等于0。

CASE
    WHEN val IS NULL THEN SELECT 'val is null'; 
    WHEN val < 0 THEN SELECT 'val is less than 0';
    WHEN val > 0 THEN SELECT 'val is greater than 0'; 
    ELSE SELECT 'val is 0';
END CASE;
AI写代码
mysql
1
2
3
4
5
6
**范例3:**声明存储过程“update_salary_by_eno4”,定义IN参数eno,输入员工编号。判断改员工所属部门,如果在10号部门,工资加500;如果在20号部门,工资加600;如果在30号部门,工资加700;其他部门加300。

CREATE PROCEDURE update_salary_by_eno4(IN eno INT) 
BEGIN
    DECLARE dept_no,emp_sal INT; 

    SELECT sal,deptno INTO emp_sal,dept_no FROM emps WHERE empno = eno; 

    CASE deptno
    WHEN 10
        THEN UPDATE emps SET sal=emp_sal+500 WHERE empno = eno; 
    WHEN 20
        THEN UPDATE emps SET sal=emp_sal+600 WHERE empno = eno; 
    WHEN 30
        THEN UPDATE emps SET sal=emp_sal+700 WHERE empno = eno;
    ELSE 
        UPDATE emps SET sal=emp_sal+300 WHERE empno = eno;    
    END CASE;
END //
AI写代码
mysql

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
**范例4:**声明存储过程pro_sal_grade,定义IN参数eno,输入员工编号。判断该员工的薪资等级,如果在[700,1200]之间,为等级一;如果在[1201,1400]之间,为等级二;如果在[1401,2000]之间,为等级三;如果在[2001,3000]之间,为等级四;如果在[3001,9999]之间,为等级五;

CREATE PROCEDURE sal_grade(IN eno INT) 
BEGIN
    DECLARE emp_sal INT; 

    SELECT sal INTO emp_sal FROM emps WHERE empno = eno;

    CASE 
        WHEN emp_sal>=700 AND emp_sal<=1200
            THEN SELECT '等级一';             
        WHEN emp_sal>=1201 AND emp_sal<=1400 
            THEN SELECT '等级二';        
        WHEN emp_sal>=1401 AND emp_sal<=2000     
            THEN SELECT '等级三';
        WHEN emp_sal>=2001 AND emp_sal<=3000 
            THEN SELECT '等级四';
        WHEN emp_sal>=3001 AND emp_sal<=999 
            THEN SELECT '等级五';
    END CASE;
END //
AI写代码
mysql

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
1.3 循环结构之 LOOP
LOOP循环语句用来重复执行某些语句。LOOP内的语句一直重复执行直到循环被退出(使用LEAVE子 句),跳出循环过程。

LOOP语句的基本格式如下:

[loop_label:] LOOP
        循环执行的语句
END LOOP [loop_label] 
AI写代码
mysql
1
2
3
其中,loop_label表示LOOP语句的标注名称,该参数可以省略。

**范例1:**使用LOOP语句进行循环操作,id值小于10时将重复执行循环过程。

DECLARE id INT DEFAULT 0;
add_loop:LOOP
    SET id = id +1;
    IF id >= 10 
    THEN LEAVE add_loop; 
    END IF;

END LOOP add_loop;
AI写代码
mysql
1
2
3
4
5
6
7
8
**范例2:**当市场环境变好时,公司为了奖励大家,决定给大家涨工资。声明存储过程“update_sal_loop()”,声明OUT参数num,输出循环次数。存储过程中实现循环给大家涨薪,薪资涨为原来的1.1倍。直到全公司的平均薪资达到8000结束。并统计循环次数。

CREATE PROCEDURE update_sal_loop(OUT num INT) 
BEGIN
    DECLARE avg_sal DOUBLE; 
    DECLARE loop_count INT DEFAULT 0;

    SELECT AVG(sal) INTO avg_sal FROM emps;

    label_loop:LOOP
        IF avg_sal >= 8000 
        THEN LEAVE label_loop;   #离开语句
        END IF;

        UPDATE emps SET sal = sal * 1.1; 
        SET loop_count = loop_count + 1;
        SELECT AVG(sal) INTO avg_salary FROM emps; 
    END LOOP label_loop;

SET num = loop_count; 
END ;
AI写代码
mysql

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
1.4 循环结构之 WHILE
WHILE语句创建一个带条件判断的循环过程。WHILE在执行语句执行时,先对指定的表达式进行判断,如 果为真,就执行循环内的语句,否则退出循环。WHILE语句的基本格式如下:

[while_label:] WHILE 循环条件 DO
    循环体
END WHILE [while_label];
AI写代码
mysql
1
2
3
while_label为WHILE语句的标注名称;如果循环条件结果为真,WHILE语句内的语句或语句群被执行,直 至循环条件为假,退出循环。

**范例1:**WHILE语句示例,i值小于10时,将重复执行循环过程,代码如下:

CREATE PROCEDURE test_while()
BEGIN
    DECLARE i INT DEFAULT 0;

    WHILE i < 10 DO 
        SET i = i + 1;
    END WHILE;

    SELECT i;
END ;
#调用
CALL test_while();
AI写代码
mysql

1
2
3
4
5
6
7
8
9
10
11
12
**范例2:**市场环境不好时,公司为了渡过难关,决定暂时降低大家的薪资。声明存储过程“update_salary_while()”,声明OUT参数num,输出循环次数。存储过程中实现循环给大家降薪,薪资降为原来的90%。直到全公司的平均薪资达到3000结束。并统计循环次数。

CREATE PROCEDURE update_sal_while(OUT num INT) 
BEGIN
    DECLARE avg_sal DOUBLE ;
    DECLARE while_count INT DEFAULT 0;
    SELECT AVG(sal) INTO avg_sal FROM emps; 
    WHILE avg_sal > 3000 DO
        UPDATE emps SET sal = sal * 0.9; 
        SET while_count = while_count + 1;
        SELECT AVG(sal) INTO avg_sal FROM emps; 
    END WHILE;

    SET num = while_count; 
END //
AI写代码
mysql

1
2
3
4
5
6
7
8
9
10
11
12
13
1.5 循环结构之 REPEAT
REPEAT语句创建一个带条件判断的循环过程。与WHILE循环不同的是,REPEAT 循环首先会执行一次循环,然后在 UNTIL 中进行表达式的判断,如果满足条件就退出,即 END REPEAT;如果条件不满足,则会就继续执行循环,直到满足退出条件为止。

REPEAT语句的基本格式如下:

[repeat_label:] REPEAT
        循环体的语句
UNTIL 结束循环的条件表达式
END REPEAT [repeat_label]
AI写代码
mysql
1
2
3
4
repeat_label为REPEAT语句的标注名称,该参数可以省略;REPEAT语句内的语句或语句群被重复,直至expr_condition为真。

范例1:

CREATE PROCEDURE test_repeat() 
BEGIN
    DECLARE i INT DEFAULT 0;

    REPEAT
        SET i = i + 1; 
    UNTIL i >= 10
    END REPEAT;

    SELECT i;
END ;
AI写代码
mysql

1
2
3
4
5
6
7
8
9
10
11
**范例2:**当市场环境变好时,公司为了奖励大家,决定给大家涨工资。声明存储过程“update_salary_repeat()”,声明OUT参数num,输出循环次数。存储过程中实现循环给大家涨薪,薪资涨为原来的1.15倍。直到全公司的平均薪资达到9000结束。并统计循环次数。

CREATE PROCEDURE update_salary_repeat(OUT num INT) 
BEGIN
    DECLARE avg_sal DOUBLE ;
    DECLARE repeat_count INT DEFAULT 0;
    
    SELECT AVG(sal) INTO avg_sal FROM emps; 
    
    REPEAT
        UPDATE emps SET sal = sal * 1.15;
        SET repeat_count = repeat_count + 1;
        SELECT AVG(sal) INTO avg_sal FROM emps; 
    UNTIL avg_sal >= 9000
    END REPEAT;

    SET num = repeat_count; 
END //
AI写代码
mysql

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
对比三种循环结构:

1、这三种循环都可以省略名称,但如果循环中添加了循环控制语句(LEAVE或ITERATE)则必须添加名称。

2、 LOOP:一般用于实现简单的"死"循环 WHILE:先判断后执行 REPEAT:先执行后判断,无条件至少执行一次

1.6 跳转语句之 LEAVE语句
LEAVE语句:可以用在循环语句内,或者以 BEGIN 和 END 包裹起来的程序体内,表示跳出循环或者跳出程序体的操作(相当于break);

基本格式如下:

LEAVE 标记名
AI写代码
mysql
1
其中,label参数表示循环的标志。LEAVE和BEGIN … END或循环一起被使用。

**范例1:**创建存储过程 “leave_begin()”,声明INT类型的IN参数num。给BEGIN…END加标记名,并在BEGIN…END中使用IF语句判断num参数的值。

如果num<=0,则使用LEAVE语句退出BEGIN…END;
如果num=1,则查询“emps”表的平均薪资;
如果num=2,则查询“emps”表的最低薪资;
如果num>2,则查询“emps”表的最高薪资。
IF语句结束后查询“emp”表的总人数。

CREATE PROCEDURE leave_begin(IN num INT) 
    begin_label: BEGIN
        IF num<=0
            THEN LEAVE begin_label; 
        ELSEIF num=1
            THEN SELECT AVG(sal) FROM emps; 
        ELSEIF num=2
            THEN SELECT MIN(sal) FROM emps; 
        ELSE
            SELECT MAX(sal) FROM emps; 
        END IF;

        SELECT COUNT(*) FROM emps; 
END ;
AI写代码
mysql

1
2
3
4
5
6
7
8
9
10
11
12
13
14
**范例2:**当市场环境不好时,公司为了渡过难关,决定暂时降低大家的薪资。声明存储过程“leave_while()”,声明OUT参数num,输出循环次数,存储过程中使用WHILE循环给大家降低薪资为原来薪资的90%,直到全公 司的平均薪资小于等于10000,并统计循环次数。

CREATE PROCEDURE leave_while(OUT num INT)
BEGIN
    #
    DECLARE avg_sal DOUBLE;#记录平均工资
    DECLARE while_count INT DEFAULT 0; #记录循环次数

    SELECT AVG(sal) INTO avg_sal FROM emps; #① 初始化条件

    while_label:WHILE TRUE DO #② 循环条件

        #③ 循环体
        IF avg_sal <= 10000 
            THEN LEAVE while_label;
        END IF;

        UPDATE emps SET sal = sal * 0.9; 
        SET while_count = while_count + 1;

        #④ 迭代条件
        SELECT AVG(sal) INTO avg_sal FROM emps; 
    END WHILE;
    #赋值
    SET num = while_count; 
END ;
AI写代码
mysql

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
1.7 跳转语句之 ITERATE 语句
ITERATE(iterate)语句:只能用在循环语句(LOOP、REPEAT和WHILE语句)内,表示重新开始循环,将执行顺序 转到语句段开头处(continue).

语句基本格式如下:

ITERATE label
AI写代码
mysql
1
label参数表示循环的标志。ITERATE语句必须跟在循环标志前面。

范例: 定义局部变量num,初始值为0。循环结构中执行num + 1操作。

如果num < 10,则继续执行循环;
如果num > 15,则退出循环结构;
CREATE PROCEDURE test_iterate() 
BEGIN
    DECLARE num INT DEFAULT 0;

    my_loop:LOOP
        SET num = num + 1;

        IF num < 10
            THEN ITERATE my_loop; 
        ELSEIF num > 15
            THEN LEAVE my_loop; 
        END IF;

        SELECT '顿开教育:让每个学员都学有所成'; 
    END LOOP my_loop;
END //
AI写代码
mysql

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2. 游标
2.1 什么是游标(或光标)
虽然我们也可以通过筛选条件 WHERE 和 HAVING,或者是限定返回记录的关键字 LIMIT 返回一条记录, 但是,却无法在结果集中像指针一样,向前定位一条记录、向后定位一条记录,或者是随意定位到某一条记录 ,并对记录的数据进行处理。

这个时候,就可以用到游标。在 SQL 中,游标是一种临时的数据库对象,可以指向存储在数据库表中的数据行指针。说白了就是可以逐行读取。

MySQL中游标可以在存储过程和函数中使用。

比如,我们查询了 emps数据表中工资高于1500的员工都有哪些:

SELECT empno,ename,sal FROM emps WHERE sal > 1500;
AI写代码
mysql
1


这里我们就可以通过游标来操作数据行,如图所示此时游标所在的行是“7698”的记录,我们也可以在结果集上滚动游标,指向结果集中的任意一行。

4.2 使用游标步骤
游标必须在声明处理程序之前被声明,并且变量和条件还必须在声明游标或处理程序之前被声明。 如果我们想要使用游标,一般需要经历四个步骤。

第一步,声明游标

在MySQL中,使用DECLARE关键字来声明游标,其语法的基本形式如下:

DECLARE cursor_name CURSOR FOR select_statement;
AI写代码
mysql
1
这个语法适用于 MySQL,SQL Server,DB2 和 MariaDB。如果是用 Oracle 或者 PostgreSQL,需要写成:

DECLARE cursor_name CURSOR IS select_statement;
AI写代码
mysql
1
要使用 SELECT 语句来获取数据结果集,而此时还没有开始遍历数据,这里 select_statement 代表的是

SELECT 语句,返回一个用于创建游标的结果集。

比如:

DECLARE cur_emp CURSOR FOR
SELECT empno,sal FROM emps;
AI写代码
mysql
1
2
DECLARE cursor_dept CURSOR FOR 
SELECT deptno,dname, loc FROM depts ;
AI写代码
mysql
1
2
第二步,打开游标

打开游标的语法如下:

OPEN cursor_name
AI写代码
mysql
1
当我们定义好游标之后,如果想要使用游标,必须先打开游标。

打开游标的时候 SELECT 语句的查询结果集就会送到游标工作区,为后面游标的 逐条读取 结果集中的记录做准备。

OPEN cur_emp ;
AI写代码
mysql
1
第三步,使用游标(从游标中取得数据)

语法如下:

FETCH cursor_name INTO var_name [, var_name] ...
AI写代码
mysql
1
这句的作用是使用 cursor_name 这个游标来读取当前行,并且将数据保存到 var_name 这个变量中,游标指针指到下一行。如果游标读取的数据行有多个列名,则在 INTO 关键字后面赋值给多个变量名即可。

注意:var_name必须在声明游标之前就定义好。

FETCH cur_emp INTO emp_no, emp_sal ;
AI写代码
mysql
1
注意:游标的查询结果集中的字段数,必须跟 INTO 后面的变量数一致,否则,在存储过程执行的时候,MySQL 会提示错误。

第四步,关闭游标

CLOSE cursor_name
AI写代码
mysql
1
有 OPEN 就会有 CLOSE,也就是打开和关闭游标。当我们使用完游标后需要关闭掉该游标。因为游标会占用系统资源 ,如果不及时关闭,游标会一直保持到存储过程结束,影响系统运行的效率。而关闭游标的操作,会释放游标占用的系统资源。

关闭游标之后,我们就不能再检索查询结果中的数据行,如果需要检索只能再次打开游标。

CLOSE cur_emp; 
AI写代码
mysql
1
2.3 举例
创建存储过程“get_count_by_limit_total_salary()”,声明IN参数 limit_total_salary;声明OUT参数total_count。函数的功能可以实现累加薪资最高的几个员工的薪资值,直到薪资总和 达到limit_total_salary参数的值,返回累加的人数给total_count。

CREATE PROCEDURE get_count_by_limit_total_salary(IN limit_total_sal INT,OUT total_count INT)
BEGIN
    #声明变量
    DECLARE sum_sal INT DEFAULT  0;        #记录累加的总工资
    DECLARE emp_count INT DEFAULT  0;    #记录循环总次数
    DECLARE emp_sal INT;                            #当前员工的工资
————————————————

                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
                        
原文链接:https://blog.youkuaiyun.com/weixin_74085818/article/details/148659979

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值