鸿扬家装网站建设,保洁公司用哪些网站做推广,苏州著名网站建设,安卓应用开发工具视频链接#xff1a;【MySQL数据库入门到大牛#xff0c;mysql安装到优化#xff0c;百科全书级#xff0c;全网天花板】 文章目录 第16章_变量、流程控制与游标1. 变量1.1 系统变量1.1.1 系统变量分类1.1.2 查看系统变量 1.2 用户变量1.2.1 用户变量分类1.2.2 会话用户变量… 视频链接【MySQL数据库入门到大牛mysql安装到优化百科全书级全网天花板】 文章目录 第16章_变量、流程控制与游标1. 变量1.1 系统变量1.1.1 系统变量分类1.1.2 查看系统变量 1.2 用户变量1.2.1 用户变量分类1.2.2 会话用户变量1.2.3 局部变量1.2.4 对比会话用户变量与局部变量 2. 定义条件与处理程序2.1 案例分析2.2 定义条件2.3 定义处理程序2.4 案例解决 3. 流程控制3.1 分支结构之 IF3.2 分支结构之 CASE3.3 循环结构之LOOP3.4 循环结构之WHILE3.5 循环结构之REPEAT3.6 跳转语句之LEAVE语句3.7 跳转语句之ITERATE语句 4. 游标4.1 什么是游标或光标4.2 使用游标步骤4.3 举例4.5 小结 补充MySQL 8.0的新特性—全局变量的持久化 第16章_变量、流程控制与游标
讲师尚硅谷-宋红康江湖人称康师傅
官网http://www.atguigu.com 1. 变量
在MySQL数据库的存储过程和函数中可以使用变量来存储查询或计算的中间结果数据或者输出最终的结果数据。
在 MySQL 数据库中变量分为系统变量以及用户自定义变量。
1.1 系统变量
1.1.1 系统变量分类
变量由系统定义不是用户定义属于服务器层面。启动MySQL服务生成MySQL服务实例期间MySQL将为MySQL服务器内存中的系统变量赋值这些系统变量定义了当前MySQL服务实例的属性、特征。这些系统变量的值要么是编译MySQL时参数的默认值要么是配置文件例如my.ini等中的参数值。大家可以通过网址 https://dev.mysql.com/doc/refman/8.0/en/server-system-variables.html 查看MySQL文档的系统变量。
系统变量分为全局系统变量需要添加global 关键字以及会话系统变量需要添加 session 关键字有时也把全局系统变量简称为全局变量有时也把会话系统变量称为local变量。**如果不写默认会话级别。**静态变量在 MySQL 服务实例运行期间它们的值不能使用 set 动态修改属于特殊的全局系统变量。
每一个MySQL客户机成功连接MySQL服务器后都会产生与之对应的会话。会话期间MySQL服务实例会在MySQL服务器内存中生成与该会话对应的会话系统变量这些会话系统变量的初始值是全局系统变量值的复制。如下图 全局系统变量针对于所有会话连接有效但不能跨重启会话系统变量仅针对于当前会话连接有效。会话期间当前会话对某个会话系统变量值的修改不会影响其他会话同一个会话系统变量的值。会话1对某个全局系统变量值的修改会导致会话2中同一个全局系统变量值的修改。
在MySQL中有些系统变量只能是全局的例如 max_connections 用于限制服务器的最大连接数有些系统变量作用域既可以是全局又可以是会话例如 character_set_client 用于设置客户端的字符集有些系统变量的作用域只能是当前会话例如 pseudo_thread_id 用于标记当前会话的 MySQL 连接 ID。
1.1.2 查看系统变量
查看所有或部分系统变量
#查看所有全局变量
SHOW GLOBAL VARIABLES;#查看所有会话变量
SHOW SESSION VARIABLES;
或
SHOW VARIABLES;#查看满足条件的部分系统变量。
SHOW GLOBAL VARIABLES LIKE %标识符%;#查看满足条件的部分会话变量
SHOW SESSION VARIABLES LIKE %标识符%;举例
SHOW GLOBAL VARIABLES LIKE admin_%;查看指定系统变量
作为 MySQL 编码规范MySQL 中的系统变量以两个“”开头其中“global”仅用于标记全局系统变量“session”仅用于标记会话系统变量。“”首先标记会话系统变量如果会话系统变量不存在则标记全局系统变量。
#查看指定的系统变量的值
SELECT global.变量名;#查看指定的会话变量的值
SELECT session.变量名;
#或者
SELECT 变量名;修改系统变量的值
有些时候数据库管理员需要修改系统变量的默认值以便修改当前会话或者MySQL服务实例的属性、特征。具体方法
方式1修改MySQL配置文件继而修改MySQL系统变量的值该方法需要重启MySQL服务
方式2在MySQL服务运行期间使用“set”命令重新设置系统变量的值
#为某个系统变量赋值
#方式1
SET global.变量名变量值;
#方式2
SET GLOBAL 变量名变量值;#为某个会话变量赋值
#方式1
SET session.变量名变量值;
#方式2
SET SESSION 变量名变量值;举例
SELECT global.autocommit;
SET GLOBAL autocommit0;SELECT session.tx_isolation;
SET session.tx_isolationread-uncommitted;SET GLOBAL max_connections 1000;
SELECT global.max_connections;1.2 用户变量
1.2.1 用户变量分类
用户变量是用户自己定义的作为 MySQL 编码规范MySQL 中的用户变量以一个“”开头。根据作用范围不同又分为会话用户变量和局部变量。 会话用户变量作用域和会话变量一样只对当前连接会话有效。 局部变量只在 BEGIN 和 END 语句块中有效。局部变量只能在存储过程和函数中使用。
1.2.2 会话用户变量
变量的定义
#方式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值1.2.3 局部变量
定义可以使用DECLARE语句定义一个局部变量
作用域仅仅在定义它的 BEGIN … END 中有效
位置只能放在 BEGIN … END 中而且只能放在第一句
BEGIN#声明局部变量DECLARE 变量名1 变量数据类型 [DEFAULT 变量默认值];DECLARE 变量名2,变量名3,... 变量数据类型 [DEFAULT 变量默认值];#为局部变量赋值SET 变量名1 值;SELECT 值 INTO 变量名2 [FROM 子句];#查看局部变量的值SELECT 变量1,变量2,变量3;
END
1.定义变量
DECLARE 变量名 类型 [default 值]; # 如果没有DEFAULT子句初始值为NULL举例
DECLARE myparam INT DEFAULT 100;2.变量赋值
方式1一般用于赋简单的值
SET 变量名值;
SET 变量名:值;方式2一般用于赋表中的字段值
SELECT 字段名或表达式 INTO 变量名 FROM 表;3.使用变量查看、比较、运算等
SELECT 局部变量名;举例1声明局部变量并分别赋值为employees表中employee_id为102的last_name和salary
DELIMITER //CREATE PROCEDURE set_value()
BEGINDECLARE emp_name VARCHAR(25);DECLARE sal DOUBLE(10,2);SELECT last_name,salary INTO emp_name,salFROM employees WHERE employee_id 102;SELECT emp_name,sal;
END //DELIMITER ;举例2声明两个变量求和并打印 分别使用会话用户变量、局部变量的方式实现
#方式1使用用户变量
SET m1;
SET n1;
SET summn;SELECT sum;#方式2使用局部变量
DELIMITER //CREATE PROCEDURE add_value()
BEGIN#局部变量DECLARE m INT DEFAULT 1;DECLARE n INT DEFAULT 3;DECLARE SUM INT;SET SUM mn;SELECT SUM;
END //DELIMITER ;举例3创建存储过程“different_salary”查询某员工和他领导的薪资差距并用IN参数emp_id接收员工id用OUT参数dif_salary输出薪资差距结果。
#声明
DELIMITER //CREATE PROCEDURE different_salary(IN emp_id INT,OUT dif_salary DOUBLE)
BEGIN#声明局部变量DECLARE emp_sal,mgr_sal DOUBLE DEFAULT 0.0;DECLARE mgr_id INT;SELECT salary INTO emp_sal FROM employees WHERE employee_id emp_id;SELECT manager_id INTO mgr_id FROM employees WHERE employee_id emp_id;SELECT salary INTO mgr_sal FROM employees WHERE employee_id mgr_id;SET dif_salary mgr_sal - emp_sal;END //DELIMITER ;#调用
SET emp_id 102;
CALL different_salary(emp_id,diff_sal);#查看
SELECT diff_sal;1.2.4 对比会话用户变量与局部变量 作用域 定义位置 语法
会话用户变量 当前会话 会话的任何地方 加符号不用指定类型
局部变量 定义它的BEGIN END中 BEGIN END的第一句话 一般不用加,需要指定类型2. 定义条件与处理程序
定义条件是事先定义程序执行过程中可能遇到的问题处理程序定义了在遇到问题时应当采取的处理方式并且保证存储过程或函数在遇到警告或错误时能继续执行。这样可以增强存储程序处理问题的能力避免程序异常停止运行。
说明定义条件和处理程序在存储过程、存储函数中都是支持的。
2.1 案例分析
**案例分析**创建一个名称为“UpdateDataNoCondition”的存储过程。代码如下
DELIMITER //CREATE PROCEDURE UpdateDataNoCondition()BEGINSET 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 ;调用存储过程
mysql CALL UpdateDataNoCondition();
ERROR 1048 (23000): Column email cannot be nullmysql SELECT x;
------
| x |
------
| 1 |
------
1 row in set (0.00 sec)
可以看到此时x变量的值为1。结合创建存储过程的SQL语句代码可以得出在存储过程中未定义条件和处理程序且当存储过程中执行的SQL语句报错时MySQL数据库会抛出错误并退出当前SQL逻辑不再向下继续执行。
2.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_codeHY000’是sqlstate_value。例如在ERROR 114242000中1142是MySQL_error_code42000’是sqlstate_value。
**举例1**定义“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;**举例2**定义ERROR 1148(42000)错误名称为command_not_allowed。
#使用MySQL_error_code
DECLARE command_not_allowed CONDITION FOR 1148;#使用sqlstate_value
DECLARE command_not_allowed CONDITION FOR SQLSTATE 42000;2.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;2.4 案例解决
在存储过程中定义处理程序捕获sqlstate_value值当遇到MySQL_error_code值为1048时执行CONTINUE操作并且将proc_value的值设置为-1。
DELIMITER //CREATE 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;END //DELIMITER ;调用过程
mysql CALL UpdateDataWithCondition();
Query OK, 0 rows affected (0.01 sec)mysql SELECT x,proc_value;
-------------------
| x | proc_value |
-------------------
| 3 | -1 |
-------------------
1 row in set (0.00 sec)
举例
创建一个名称为“InsertDataWithCondition”的存储过程代码如下。
在存储过程中定义处理程序捕获sqlstate_value值当遇到sqlstate_value值为23000时执行EXIT操作并且将proc_value的值设置为-1。
#准备工作
CREATE TABLE departments
AS
SELECT * FROM atguigudb.departments;ALTER TABLE departments
ADD CONSTRAINT uk_dept_name UNIQUE(department_id);DELIMITER //CREATE PROCEDURE InsertDataWithCondition()BEGINDECLARE duplicate_entry CONDITION FOR SQLSTATE 23000 ;DECLARE EXIT HANDLER FOR duplicate_entry SET proc_value -1;SET x 1;INSERT INTO departments(department_name) VALUES(测试);SET x 2;INSERT INTO departments(department_name) VALUES(测试);SET x 3;END //DELIMITER ;调用存储过程
mysql CALL InsertDataWithCondition();
Query OK, 0 rows affected (0.01 sec)mysql SELECT x,proc_value;
-------------------
| x | proc_value |
-------------------
| 2 | -1 |
-------------------
1 row in set (0.00 sec)
3. 流程控制
解决复杂问题不可能通过一个 SQL 语句完成我们需要执行多个 SQL 操作。流程控制语句的作用就是控制存储过程中 SQL 语句的执行顺序是我们完成复杂操作必不可少的一部分。只要是执行的程序流程就分为三大类
顺序结构程序从上往下依次执行分支结构程序按条件进行选择执行从两条或多条路径中选择一条执行循环结构程序满足一定条件下重复执行一组语句
针对于MySQL 的流程控制语句主要有 3 类。注意只能用于存储程序。
条件判断语句IF 语句和 CASE 语句循环语句LOOP、WHILE 和 REPEAT 语句跳转语句ITERATE 和 LEAVE 语句
3.1 分支结构之 IF
IF 语句的语法结构是
IF 表达式1 THEN 操作1
[ELSEIF 表达式2 THEN 操作2]……
[ELSE 操作N]
END IF根据表达式的结果为TRUE或FALSE执行相应的语句。这里“[]”中的内容是可选的。 特点① 不同的表达式对应不同的操作 ② 使用在begin end中 举例1 IF val IS NULL THEN SELECT val is null;
ELSE SELECT val is not null;END IF;**举例2**声明存储过程“update_salary_by_eid1”定义IN参数emp_id输入员工编号。判断该员工薪资如果低于8000元并且入职时间超过5年就涨薪500元否则就不变。 DELIMITER //CREATE PROCEDURE update_salary_by_eid1(IN emp_id INT)
BEGINDECLARE emp_salary DOUBLE;DECLARE hire_year DOUBLE;SELECT salary INTO emp_salary FROM employees WHERE employee_id emp_id;SELECT DATEDIFF(CURDATE(),hire_date)/365 INTO hire_yearFROM employees WHERE employee_id emp_id;IF emp_salary 8000 AND hire_year 5THEN UPDATE employees SET salary salary 500 WHERE employee_id emp_id;END IF;
END //DELIMITER ;**举例3**声明存储过程“update_salary_by_eid2”定义IN参数emp_id输入员工编号。判断该员工薪资如果低于9000元并且入职时间超过5年就涨薪500元否则就涨薪100元。 DELIMITER //CREATE PROCEDURE update_salary_by_eid2(IN emp_id INT)
BEGINDECLARE emp_salary DOUBLE;DECLARE hire_year DOUBLE;SELECT salary INTO emp_salary FROM employees WHERE employee_id emp_id;SELECT DATEDIFF(CURDATE(),hire_date)/365 INTO hire_yearFROM employees WHERE employee_id emp_id;IF emp_salary 8000 AND hire_year 5THEN UPDATE employees SET salary salary 500 WHERE employee_id emp_id;ELSE UPDATE employees SET salary salary 100 WHERE employee_id emp_id;END IF;
END //DELIMITER ;**举例4**声明存储过程“update_salary_by_eid3”定义IN参数emp_id输入员工编号。判断该员工薪资如果低于9000元就更新薪资为9000元薪资如果大于等于9000元且低于10000的但是奖金比例为NULL的就更新奖金比例为0.01其他的涨薪100元。 DELIMITER //CREATE PROCEDURE update_salary_by_eid3(IN emp_id INT)
BEGINDECLARE emp_salary DOUBLE;DECLARE bonus DECIMAL(3,2);SELECT salary INTO emp_salary FROM employees WHERE employee_id emp_id;SELECT commission_pct INTO bonus FROM employees WHERE employee_id emp_id;IF emp_salary 9000THEN UPDATE employees SET salary 9000 WHERE employee_id emp_id;ELSEIF emp_salary 10000 AND bonus IS NULLTHEN UPDATE employees SET commission_pct 0.01 WHERE employee_id emp_id;ELSEUPDATE employees SET salary salary 100 WHERE employee_id emp_id;END IF;
END //DELIMITER ;3.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后面不需要CASE 语句的语法结构2
#情况二类似于多重if
CASE
WHEN 条件1 THEN 结果1或语句1(如果是语句需要加分号)
WHEN 条件2 THEN 结果2或语句2(如果是语句需要加分号)
...
ELSE 结果n或语句n(如果是语句需要加分号)
END [case]如果是放在begin end中需要加上case如果放在select后面不需要举例1
使用CASE流程控制语句的第1种格式判断val值等于1、等于2或者两者都不等。
CASE valWHEN 1 THEN SELECT val is 1;WHEN 2 THEN SELECT val is 2;ELSE SELECT val is not 1 or 2;
END CASE;举例2
使用CASE流程控制语句的第2种格式判断val是否为空、小于0、大于0或者等于0。
CASEWHEN 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;**举例3**声明存储过程“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)
BEGINDECLARE emp_sal DOUBLE;DECLARE bonus DECIMAL(3,2);SELECT salary INTO emp_sal FROM employees WHERE employee_id emp_id;SELECT commission_pct INTO bonus FROM employees WHERE employee_id emp_id;CASEWHEN emp_sal9000THEN UPDATE employees SET salary9000 WHERE employee_id emp_id;WHEN emp_sal10000 AND bonus IS NULLTHEN UPDATE employees SET commission_pct0.01 WHERE employee_id emp_id;ELSEUPDATE employees SET salarysalary100 WHERE employee_id emp_id;END CASE;
END //DELIMITER ;举例4声明存储过程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)
BEGINDECLARE emp_sal DOUBLE;DECLARE hire_year DOUBLE;SELECT salary INTO emp_sal FROM employees WHERE employee_id emp_id;SELECT ROUND(DATEDIFF(CURDATE(),hire_date)/365) INTO hire_year FROM employees WHERE employee_id emp_id;CASE hire_yearWHEN 0 THEN UPDATE employees SET salarysalary50 WHERE employee_id emp_id;WHEN 1 THEN UPDATE employees SET salarysalary100 WHERE employee_id emp_id;WHEN 2 THEN UPDATE employees SET salarysalary200 WHERE employee_id emp_id;WHEN 3 THEN UPDATE employees SET salarysalary300 WHERE employee_id emp_id;WHEN 4 THEN UPDATE employees SET salarysalary400 WHERE employee_id emp_id;ELSE UPDATE employees SET salarysalary500 WHERE employee_id emp_id;END CASE;
END //DELIMITER ;3.3 循环结构之LOOP
LOOP循环语句用来重复执行某些语句。LOOP内的语句一直重复执行直到循环被退出使用LEAVE子句跳出循环过程。
LOOP语句的基本格式如下
[loop_label:] LOOP循环执行的语句
END LOOP [loop_label]其中loop_label表示LOOP语句的标注名称该参数可以省略。
举例1
使用LOOP语句进行循环操作id值小于10时将重复执行循环过程。
DECLARE id INT DEFAULT 0;
add_loop:LOOPSET id id 1;IF id 10 THEN LEAVE add_loop;END IF;END LOOP add_loop;**举例2**当市场环境变好时公司为了奖励大家决定给大家涨工资。声明存储过程“update_salary_loop()”声明OUT参数num输出循环次数。存储过程中实现循环给大家涨薪薪资涨为原来的1.1倍。直到全公司的平均薪资达到12000结束。并统计循环次数。
DELIMITER //CREATE PROCEDURE update_salary_loop(OUT num INT)
BEGINDECLARE avg_salary DOUBLE;DECLARE loop_count INT DEFAULT 0;SELECT AVG(salary) INTO avg_salary FROM employees;label_loop:LOOPIF avg_salary 12000 THEN LEAVE label_loop;END IF;UPDATE employees SET salary salary * 1.1;SET loop_count loop_count 1;SELECT AVG(salary) INTO avg_salary FROM employees;END LOOP label_loop;SET num loop_count;END //DELIMITER ;3.4 循环结构之WHILE
WHILE语句创建一个带条件判断的循环过程。WHILE在执行语句执行时先对指定的表达式进行判断如果为真就执行循环内的语句否则退出循环。WHILE语句的基本格式如下
[while_label:] WHILE 循环条件 DO循环体
END WHILE [while_label];while_label为WHILE语句的标注名称如果循环条件结果为真WHILE语句内的语句或语句群被执行直至循环条件为假退出循环。
举例1
WHILE语句示例i值小于10时将重复执行循环过程代码如下
DELIMITER //CREATE PROCEDURE test_while()
BEGIN DECLARE i INT DEFAULT 0;WHILE i 10 DOSET i i 1;END WHILE;SELECT i;
END //DELIMITER ;
#调用
CALL test_while();**举例2**市场环境不好时公司为了渡过难关决定暂时降低大家的薪资。声明存储过程“update_salary_while()”声明OUT参数num输出循环次数。存储过程中实现循环给大家降薪薪资降为原来的90%。直到全公司的平均薪资达到5000结束。并统计循环次数。
DELIMITER //CREATE PROCEDURE update_salary_while(OUT num INT)
BEGINDECLARE avg_sal DOUBLE ;DECLARE while_count INT DEFAULT 0;SELECT AVG(salary) INTO avg_sal FROM employees;WHILE avg_sal 5000 DOUPDATE employees SET salary salary * 0.9;SET while_count while_count 1;SELECT AVG(salary) INTO avg_sal FROM employees;END WHILE;SET num while_count;END //DELIMITER ;3.5 循环结构之REPEAT
REPEAT语句创建一个带条件判断的循环过程。与WHILE循环不同的是REPEAT 循环首先会执行一次循环然后在 UNTIL 中进行表达式的判断如果满足条件就退出即 END REPEAT如果条件不满足则会就继续执行循环直到满足退出条件为止。
REPEAT语句的基本格式如下
[repeat_label:] REPEAT循环体的语句
UNTIL 结束循环的条件表达式
END REPEAT [repeat_label]repeat_label为REPEAT语句的标注名称该参数可以省略REPEAT语句内的语句或语句群被重复直至expr_condition为真。
举例1
DELIMITER //CREATE PROCEDURE test_repeat()
BEGIN DECLARE i INT DEFAULT 0;REPEAT SET i i 1;UNTIL i 10END REPEAT;SELECT i;
END //DELIMITER ;**举例2**当市场环境变好时公司为了奖励大家决定给大家涨工资。声明存储过程“update_salary_repeat()”声明OUT参数num输出循环次数。存储过程中实现循环给大家涨薪薪资涨为原来的1.15倍。直到全公司的平均薪资达到13000结束。并统计循环次数。
DELIMITER //CREATE PROCEDURE update_salary_repeat(OUT num INT)
BEGINDECLARE avg_sal DOUBLE ;DECLARE repeat_count INT DEFAULT 0;SELECT AVG(salary) INTO avg_sal FROM employees;REPEATUPDATE employees SET salary salary * 1.15;SET repeat_count repeat_count 1;SELECT AVG(salary) INTO avg_sal FROM employees;UNTIL avg_sal 13000END REPEAT;SET num repeat_count;END //DELIMITER ;对比三种循环结构
1、这三种循环都可以省略名称但如果循环中添加了循环控制语句LEAVE或ITERATE则必须添加名称。 2、 LOOP一般用于实现简单的死循环 WHILE先判断后执行 REPEAT先执行后判断无条件至少执行一次
3.6 跳转语句之LEAVE语句
LEAVE语句可以用在循环语句内或者以 BEGIN 和 END 包裹起来的程序体内表示跳出循环或者跳出程序体的操作。如果你有面向过程的编程语言的使用经验你可以把 LEAVE 理解为 break。
基本格式如下
LEAVE 标记名其中label参数表示循环的标志。LEAVE和BEGIN … END或循环一起被使用。
**举例1**创建存储过程 “leave_begin()”声明INT类型的IN参数num。给BEGIN…END加标记名并在BEGIN…END中使用IF语句判断num参数的值。
如果num0则使用LEAVE语句退出BEGIN…END如果num1则查询“employees”表的平均薪资如果num2则查询“employees”表的最低薪资如果num2则查询“employees”表的最高薪资。
IF语句结束后查询“employees”表的总人数。
DELIMITER //CREATE PROCEDURE leave_begin(IN num INT)begin_label: BEGINIF num0 THEN LEAVE begin_label;ELSEIF num1 THEN SELECT AVG(salary) FROM employees;ELSEIF num2 THEN SELECT MIN(salary) FROM employees;ELSE SELECT MAX(salary) FROM employees;END IF;SELECT COUNT(*) FROM employees;END //DELIMITER ;举例2
当市场环境不好时公司为了渡过难关决定暂时降低大家的薪资。声明存储过程“leave_while()”声明OUT参数num输出循环次数存储过程中使用WHILE循环给大家降低薪资为原来薪资的90%直到全公司的平均薪资小于等于10000并统计循环次数。
DELIMITER //
CREATE PROCEDURE leave_while(OUT num INT)BEGIN #DECLARE avg_sal DOUBLE;#记录平均工资DECLARE while_count INT DEFAULT 0; #记录循环次数SELECT AVG(salary) INTO avg_sal FROM employees; #① 初始化条件while_label:WHILE TRUE DO #② 循环条件#③ 循环体IF avg_sal 10000 THENLEAVE while_label;END IF;UPDATE employees SET salary salary * 0.9;SET while_count while_count 1;#④ 迭代条件SELECT AVG(salary) INTO avg_sal FROM employees;END WHILE;#赋值SET num while_count;END //DELIMITER ;3.7 跳转语句之ITERATE语句
ITERATE语句只能用在循环语句LOOP、REPEAT和WHILE语句内表示重新开始循环将执行顺序转到语句段开头处。如果你有面向过程的编程语言的使用经验你可以把 ITERATE 理解为 continue意思为“再次循环”。
语句基本格式如下
ITERATE labellabel参数表示循环的标志。ITERATE语句必须跟在循环标志前面。
举例 定义局部变量num初始值为0。循环结构中执行num 1操作。
如果num 10则继续执行循环如果num 15则退出循环结构
DELIMITER //CREATE PROCEDURE test_iterate()BEGINDECLARE num INT DEFAULT 0;my_loop:LOOPSET 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 //DELIMITER ;4. 游标
4.1 什么是游标或光标
虽然我们也可以通过筛选条件 WHERE 和 HAVING或者是限定返回记录的关键字 LIMIT 返回一条记录但是却无法在结果集中像指针一样向前定位一条记录、向后定位一条记录或者是随意定位到某一条记录并对记录的数据进行处理。
这个时候就可以用到游标。游标提供了一种灵活的操作方式让我们能够对结果集中的每一条记录进行定位并对指向的记录中的数据进行操作的数据结构。游标让 SQL 这种面向集合的语言有了面向过程开发的能力。
在 SQL 中游标是一种临时的数据库对象可以指向存储在数据库表中的数据行指针。这里游标充当了指针的作用我们可以通过操作游标来对数据行进行操作。
MySQL中游标可以在存储过程和函数中使用。
比如我们查询了 employees 数据表中工资高于15000的员工都有哪些
SELECT employee_id,last_name,salary FROM employees
WHERE salary 15000;这里我们就可以通过游标来操作数据行如图所示此时游标所在的行是“108”的记录我们也可以在结果集上滚动游标指向结果集中的任意一行。
4.2 使用游标步骤
游标必须在声明处理程序之前被声明并且变量和条件还必须在声明游标或处理程序之前被声明。
如果我们想要使用游标一般需要经历四个步骤。不同的 DBMS 中使用游标的语法可能略有不同。
第一步声明游标
在MySQL中使用DECLARE关键字来声明游标其语法的基本形式如下
DECLARE cursor_name CURSOR FOR select_statement; 这个语法适用于 MySQLSQL ServerDB2 和 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 ;第三步使用游标从游标中取得数据
语法如下
FETCH cursor_name INTO var_name [, var_name] ...这句的作用是使用 cursor_name 这个游标来读取当前行并且将数据保存到 var_name 这个变量中游标指针指到下一行。如果游标读取的数据行有多个列名则在 INTO 关键字后面赋值给多个变量名即可。
注意var_name必须在声明游标之前就定义好。
FETCH cur_emp INTO emp_id, emp_sal ;注意游标的查询结果集中的字段数必须跟 INTO 后面的变量数一致否则在存储过程执行的时候MySQL 会提示错误。
第四步关闭游标
CLOSE cursor_name有 OPEN 就会有 CLOSE也就是打开和关闭游标。当我们使用完游标后需要关闭掉该游标。因为游标会占用系统资源如果不及时关闭游标会一直保持到存储过程结束影响系统运行的效率。而关闭游标的操作会释放游标占用的系统资源。
关闭游标之后我们就不能再检索查询结果中的数据行如果需要检索只能再次打开游标。
CLOSE cur_emp;4.3 举例
创建存储过程“get_count_by_limit_total_salary()”声明IN参数 limit_total_salaryDOUBLE类型声明OUT参数total_countINT类型。函数的功能可以实现累加薪资最高的几个员工的薪资值直到薪资总和达到limit_total_salary参数的值返回累加的人数给total_count。
DELIMITER //CREATE PROCEDURE get_count_by_limit_total_salary(IN limit_total_salary DOUBLE,OUT total_count INT)BEGINDECLARE 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_salaryEND REPEAT;SET total_count emp_count;#关闭游标CLOSE emp_cursor;END //DELIMITER ;4.5 小结
游标是 MySQL 的一个重要的功能为逐条读取结果集中的数据提供了完美的解决方案。跟在应用层面实现相同的功能相比游标可以在存储程序中使用效率高程序也更加简洁。
但同时也会带来一些性能问题比如在使用游标的过程中会对数据行进行加锁这样在业务并发量大的时候不仅会影响业务之间的效率还会消耗系统资源造成内存不足这是因为游标是在内存中进行的处理。
建议养成用完之后就关闭的习惯这样才能提高系统的整体效率。
补充MySQL 8.0的新特性—全局变量的持久化
在MySQL数据库中全局变量可以通过SET GLOBAL语句来设置。例如设置服务器语句超时的限制可以通过设置系统变量max_execution_time来实现
SET GLOBAL MAX_EXECUTION_TIME2000;使用SET GLOBAL语句设置的变量值只会临时生效。数据库重启后服务器又会从MySQL配置文件中读取变量的默认值。 MySQL 8.0版本新增了SET PERSIST命令。例如设置服务器的最大连接数为1000
SET PERSIST global max_connections 1000;MySQL会将该命令的配置保存到数据目录下的mysqld-auto.cnf文件中下次启动时会读取该文件用其中的配置来覆盖默认的配置文件。
举例
查看全局变量max_connections的值结果如下
mysql show variables like %max_connections%;
-------------------------------
| Variable_name | Value |
-------------------------------
| max_connections | 151 |
| mysqlx_max_connections | 100 |
-------------------------------
2 rows in set, 1 warning (0.00 sec)设置全局变量max_connections的值
mysql set persist max_connections1000;
Query OK, 0 rows affected (0.00 sec)重启MySQL服务器再次查询max_connections的值
mysql show variables like %max_connections%;
-------------------------------
| Variable_name | Value |
-------------------------------
| max_connections | 1000 |
| mysqlx_max_connections | 100 |
-------------------------------
2 rows in set, 1 warning (0.00 sec)补充MySQL 8.0的新特性—全局变量的持久化
在MySQL数据库中全局变量可以通过SET GLOBAL语句来设置。例如设置服务器语句超时的限制可以通过设置系统变量max_execution_time来实现
SET GLOBAL MAX_EXECUTION_TIME2000;使用SET GLOBAL语句设置的变量值只会临时生效。数据库重启后服务器又会从MySQL配置文件中读取变量的默认值。 MySQL 8.0版本新增了SET PERSIST命令。例如设置服务器的最大连接数为1000
SET PERSIST global max_connections 1000;MySQL会将该命令的配置保存到数据目录下的mysqld-auto.cnf文件中下次启动时会读取该文件用其中的配置来覆盖默认的配置文件。
举例
查看全局变量max_connections的值结果如下
mysql show variables like %max_connections%;
-------------------------------
| Variable_name | Value |
-------------------------------
| max_connections | 151 |
| mysqlx_max_connections | 100 |
-------------------------------
2 rows in set, 1 warning (0.00 sec)设置全局变量max_connections的值
mysql set persist max_connections1000;
Query OK, 0 rows affected (0.00 sec)重启MySQL服务器再次查询max_connections的值
mysql show variables like %max_connections%;
-------------------------------
| Variable_name | Value |
-------------------------------
| max_connections | 1000 |
| mysqlx_max_connections | 100 |
-------------------------------
2 rows in set, 1 warning (0.00 sec)