【MySQL】第十二章 变量 ,中断处理机制,流程控制

1,系统变量和会话变量

系统变量分为全局系统变量(需要添加 global 关键字)以及会话系统变量(需要添加 session 关键字),有时也把全局系统变量简称为全局变量,有时也把会话系统变量称为local变量。如果不写,默认会话级别静态变量(在 MySQL 服务实例运行期间它们的值不能使用 set 动态修改)属于特殊的全局系统变量

每一个MySQL客户机成功连接MySQL服务器后,都会产生与之对应的会话。会话期间,MySQL服务实例会在MySQL服务器内存中生成与该会话对应的会话系统变量,这些会话系统变量的初始值是全局系统变量值的复制。

(这里是什么意思呢?)就是当我们利用cmd命令行输入

sql 复制代码
-- 客户机打开第一个连接(会话1)
mysql -u root -p
​
-- 同一个客户机程序可以再打开另一个终端,建立第二个连接(会话2)
mysql -u root -p

的时候,然后就可以进行跟MySQL进行多个会话,每一个会话都是全局系统变量的复制,但是全局系统变量是每一个会话公用的

当人为进行重启MySQL服务器的时候,会导致修改的全局变量全部重置,所有的会话系统变量也会随之消失,当然不结束服务器的时候,结束掉一个会话,也只会导致那个会话结束

对于全局系统变量和会话系统变量的操作

sql 复制代码
#1.3 查看指定系统变量
# 全局(特有)
SELECT @@global.max_connections;
#错误:
SELECT @@session.max_connections;
​
​
#会话和全局是进行相互之间的复制的,当然后续还可以改动这些数据,所以产生了交集
SELECT @@global.character_set_client;
SELECT @@session.character_set_client;
​
​
# 
SELECT @@session.pseudo_thread_id;
#错误:
SELECT @@global.pseudo_thread_id;
​
​
SELECT @@character_set_client; #先查询会话系统变量,再查询全局系统变量
​
​
#修改系统变量的值
#全局系统变量:
#方式1:
SET @@global.max_connections = 161;
#方式2:
SET GLOBAL max_connections = 171;
​
#针对于当前的数据库实例是有效的,一旦重启mysql服务,就失效了。
​
#会话系统变量:
#方式1:
SET @@session.character_set_client = 'gbk';
#方式2:
SET SESSION character_set_client = 'gbk';
2,用户变量

用户变量是用户自己定义的,作为 MySQL 编码规范,MySQL 中的用户变量以 一个"@" 开头。根据作用范围不同,又分为会话用户变量和局部变量 。

会话用户变量:作用域和会话变量一样,只对当前连接会话有效 局部变量:只在 BEGIN 和 END 语句块中有效。局部变量只能在 存储过程和函数 中使用

会话变量的赋值操作

sql 复制代码
#方式1:"="或":="
SET @用户变量 = 值;
SET @用户变量 := 值;
​
#方式2:":=" 或 INTO关键字
SELECT @用户变量 := 表达式 [FROM 等子句];
SELECT 表达式 INTO @用户变量 [FROM 等子句];

局部变量

sql 复制代码
DELIMITER //
CREATE PROCEDURE set_value()
​
BEGIN
    # 局部变量的声明
    DECLARE emp_name VARCHAR(25);
    DECLARE sal DOUBLE(10,2) DEFAULT 0.0; # 可以用DEFAULT 来设置默认值
    
    # 对局部变量进行赋值
    SELECT last_name,salary INTO emp_name,sal
    FROM employees
    WHERE employee_id = 102;
    
    SELECT emp_name,sal;
END //
​
DELIMITER ;
​
#调用存储过程
CALL test_var();
3,程序出错的处理机制
sql 复制代码
#错误演示:
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
CALL UpdateDataNoCondition();
​
SELECT @x;
​
/*
这里的存储过程调用之后,会出错,就是第一个UPDATE语句是不可以更新email为空的,所以这会报错,但是查询@x值是1的,说明走过了这里
*/

这个例子也提示了一下可以构建局部变量进行调试

定义中断条件和处理程序

sql 复制代码
#2.2 定义条件
#格式:DECLARE 错误名称 CONDITION FOR 错误码(或错误条件)
​
ERROR 1048 (23000)
MySQL_error_code 表示的是数值类型错误代码      (1048)
sqlstate_value   是长度为5的字符串类型错误代码 (23000)
​
#举例1:定义"Field_Not_Be_NULL"错误名与MySQL中违反非空约束的错误类型
#是"ERROR 1048 (23000)"对应。
#方式1:使用MySQL_error_code
DECLARE Field_Not_Be_NULL CONDITION FOR 1048;
​
#方式2:使用sqlstate_value
DECLARE Field_Not_Be_NULL CONDITION FOR SQLSTATE '23000';
​
#举例2:定义"ERROR 1148(42000)"错误,名称为command_not_allowed。
#方式1:使用MySQL_error_code
DECLARE command_not_allowed CONDITION FOR 1148;
​
#方式2:使用sqlstate_value
DECLARE command_not_allowed CONDITION FOR SQLSTATE '42000';
sql 复制代码
#2.3 定义处理程序
#格式:DECLARE 处理方式 HANDLER FOR 错误类型 处理语句
​
#举例:
#方法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';

具体案例

sql 复制代码
在存储过程中,定义处理程序,捕获sqlstate_value值,
当遇到sqlstate_value值为23000时,执行EXIT操
作,并且将@proc_value的值设置为-1。
​
DELIMITER //
CREATE PROCEDURE InsertDataWithCondition()
    BEGIN
        DECLARE duplicate_entry CONDITION FOR SQLSTATE '23000' ;
        DECLARE EXIT HANDLER FOR duplicate_entry SET @proc_value = -1;
      # 遇到错误立马推出处理器HANDLER,然后处理的条件是
        SET @x = 1;
        INSERT INTO departments(department_name) VALUES('测试');
        SET @x = 2;
        INSERT INTO departments(department_name) VALUES('测试');
        SET @x = 3;
    END //
DELIMITER ;
4,分支结构

IF的语法结构

sql 复制代码
IF 表达式1 THEN 操作1
ELSEIF 表达式2 THEN 操作2
ELSE 操作N
END IF
#举例2:声明存储过程"update_salary_by_eid1",定义IN参数emp_id,输入员工编号。
#判断该员工薪资如果低于8000元并且入职时间超过5年,就涨薪500元;否则就不变。
​
DELIMITER //
CREATE PROCEDURE update_salary_by_eid1(IN emp_id INT)
BEGIN
    DECLARE emp_sal DOUBLE;   # 每一个员工的薪水
    DECLARE hire_year DOUBLE; # 每一个员工的入职年份
    
    # 赋值
    SELECT salary INTO emp_sal FROM employees WHERE employee_id = emp_id;
    SELECT DATETDIFF(CURDATE(),hire_year) / 365 INTO hire_year FROM employees WHERE employee_id = emp_id;
    
    # 判断 
    IF emp_sal < 8000 AND hire_year > 5
            THEN UPDATE employees set salary = salary + 500 WHERE employeed_id = emp_id;
    END IF;
END //
DELIMITER;

CASE分支结构

sql 复制代码
#情况一:类似于switch
CASE 表达式
WHEN 值1 THEN 结果1或语句1(如果是语句,需要加分号)
WHEN 值2 THEN 结果2或语句2(如果是语句,需要加分号)
...
ELSE 结果n或语句n(如果是语句,需要加分号)
END [case](如果是放在begin end中需要加上case,如果放在select后面不需要)
​
​
#情况二:类似于多重if
CASE
WHEN 条件1 THEN 结果1或语句1(如果是语句,需要加分号)
WHEN 条件2 THEN 结果2或语句2(如果是语句,需要加分号)
...
ELSE 结果n或语句n(如果是语句,需要加分号)
END [case](如果是放在begin end中需要加上case,如果放在select后面不需要)
#举例2:声明存储过程"update_salary_by_eid4",定义IN参数emp_id,输入员工编号。
#判断该员工薪资如果低于9000元,就更新薪资为9000元;薪资大于等于9000元且低于10000的,
#但是奖金比例为NULL的,就更新奖金比例为0.01;其他的涨薪100元。
DELIMITER //
CREATE PROCEDURE update_salary_by_eid4(IN emp_id INT)
BEGIN
    
    DECLARE emp_sal DOUBLE # 定义员工薪水
    DECLARE bonus DOUBLE # 定义奖金比例
    
    SELECT salary INTO emp_sal FROM employees WHERE employees_id = emp_id;
    SELECT commission_pct INTO  FROM employees WHERE employees_id = emp_id;
    
    CASE
    WHEN emp_sal < 9000 THEN UPDATE employees SET salary = 9000 WHERE employee_id = emp_id;
    WHEN emp_sal < 10000 AND bonus IS NULL THEN UPDATE employees SET commission_pct = 0.01 
                            WHERE employee_id = emp_id;
    ELSE UPDATE employees SET salary = salary + 100 WHERE employee_id = emp_id;
    END CASE;
END
DELIMITER ;
#举例3:声明存储过程update_salary_by_eid5,定义IN参数emp_id,输入员工编号。
#判断该员工的入职年限,如果是0年,薪资涨50;如果是1年,薪资涨100;
#如果是2年,薪资涨200;如果是3年,薪资涨300;如果是4年,薪资涨400;其他的涨薪500。
​
DELIMITER //
​
CREATE PROCEDURE update_salary_by_eid5(IN emp_id INT)
BEGIN
    #声明局部变量
    DECLARE hire_year INT; #记录员工入职公司的总时间(单位:年)
    
    #赋值
    SELECT ROUND(DATEDIFF(CURDATE(),hire_date) / 365) INTO hire_year 
    FROM employees WHERE employee_id = emp_id;
    
    #判断
    CASE hire_year
        WHEN 0 THEN UPDATE employees SET salary = salary + 50 WHERE employee_id = emp_id;
        WHEN 1 THEN UPDATE employees SET salary = salary + 100 WHERE employee_id = emp_id;
        WHEN 2 THEN UPDATE employees SET salary = salary + 200 WHERE employee_id = emp_id;
        WHEN 3 THEN UPDATE employees SET salary = salary + 300 WHERE employee_id = emp_id;
        WHEN 4 THEN UPDATE employees SET salary = salary + 400 WHERE employee_id = emp_id;
        ELSE UPDATE employees SET salary = salary + 500 WHERE employee_id = emp_id;
    END CASE;
END //
​
DELIMITER ;
5,循环结构

loop循环结构

sql 复制代码
[loop_label:] LOOP
    循环执行的语句
END LOOP [loop_label]
#举例2:当市场环境变好时,公司为了奖励大家,决定给大家涨工资。
#声明存储过程"update_salary_loop()",声明OUT参数num,输出循环次数。
#存储过程中实现循环给大家涨薪,薪资涨为原来的1.1倍。直到全公司的平
#均薪资达到12000结束。并统计循环次数。
​
​
DELIMITER //
CREATE PROCEDURE update_salary_loop(OUT num INT)
BEGIN
    #声明变量
    DECLARE avg_sal DOUBLE ; #记录员工的平均工资
    DECLARE loop_count INT DEFAULT 0;#记录循环的次数
    
    #① 初始化条件
    #获取员工的平均工资
    SELECT AVG(salary) INTO avg_sal FROM employees;
    
    loop_lab:LOOP
        #② 循环条件
        #结束循环的条件
        IF avg_sal >= 12000
            THEN LEAVE loop_lab;
        END IF;
        
        #③ 循环体
        #如果低于12000,更新员工的工资
        UPDATE employees SET salary = salary * 1.1;
        
        #④ 迭代条件
        #更新avg_sal变量的值
        SELECT AVG(salary) INTO avg_sal FROM employees;
        
        #记录循环次数
        SET loop_count = loop_count + 1;
    END LOOP loop_lab;
    SET num = loop_count;
END//
DELIMITER ;

while与repeat循环结构

sql 复制代码
# while 循环
[while_label:] WHILE 循环条件  DO
    循环体
END WHILE [while_label];
# do while 循环
[repeat_label:] REPEAT
    循环体的语句
UNTIL 结束循环的条件表达式
END REPEAT [repeat_label]
6,循环终止条件

leave和iterate

sql 复制代码
leave 一般后面接循环标签lable        break     用于终止循环
iterate 一般后面也是接循环标签lable  continue  用于跳过后续直接进入下一个循环
DELIMITER //
​
CREATE PROCEDURE leave_begin(IN num INT)
​
begin_label:BEGIN
    IF num <= 0
        THEN LEAVE begin_label;
    ELSEIF num = 1
        THEN SELECT AVG(salary) FROM employees;
    ELSEIF num = 2
        THEN SELECT MIN(salary) FROM employees;
    ELSE 
        SELECT MAX(salary) FROM employees;
    END IF;
    
    #查询总人数
    SELECT COUNT(*) FROM employees;
​
END //
7,游标

游标的使用过程

sql 复制代码
声明游标 DECLARE 游标名称 CURSOR FOR (SELECT语句, 表示要处理的一个数据表)
打开游标 OPEN 游标名称
使用游标 FETCH 游标名称 INTO 变量名  将当前游标所对应的数据转移到变量里
#游标本身不会自动移动,是每次执行 FETCH 语句时,游标才会向下移动一行
关闭游标 CLOSE 游标的名称
#举例:创建存储过程"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_sal DOUBLE DEFAULT 0.0; #记录累加的工资总额
    DECLARE emp_sal DOUBLE; #记录每一个员工的工资
    DECLARE emp_count INT DEFAULT 0;#记录累加的人数
    
    #1.声明游标
    DECLARE emp_cursor CURSOR FOR SELECT salary FROM employees ORDER BY salary DESC;
    #2.打开游标
    OPEN emp_cursor;
    REPEAT
        #3.使用游标
        FETCH emp_cursor INTO emp_sal;
​
        SET sum_sal = sum_sal + emp_sal;
        SET emp_count = emp_count + 1;
        UNTIL sum_sal >= limit_total_salary
    END REPEAT;
    
    SET total_count = emp_count;
    #4.关闭游标
    CLOSE emp_cursor;
END //
DELIMITER ;
相关推荐
仍然.2 小时前
MySQL--数据库基础
数据库·mysql
是三好3 小时前
MySQL
数据库·mysql·oracle
会飞的灰大狼3 小时前
MySQL增量备份实战指南
数据库·mysql
宸津-代码粉碎机3 小时前
用MySQL玩转数据可视化
数据库·mysql·信息可视化
李慕婉学姐4 小时前
【开题答辩过程】以《基于uniapp的养宠互助服务程序设计与实现》为例,不知道这个选题怎么做的,不知道这个选题怎么开题答辩的可以进来看看
android·mysql·uni-app
码农水水4 小时前
京东Java面试被问:分布式会话的一致性和容灾方案
java·开发语言·数据库·分布式·mysql·面试·职场和发展
warton884 小时前
ubuntu24实现单节点mysql mgr配置
数据库·mysql
PD我是你的真爱粉5 小时前
MySQL基础-DDL语句
数据库·mysql·oracle
L1624765 小时前
MySQL 8.0+ MHA 高可用集群搭建(生产环境级・超详细)
数据库·mysql