【MySQL基础】七.mysql基础--变量、流程控制与游标

一.变量

1.分类:

①系统变量
②自定义变量

2.系统变量

2.1系统变量分类

①全局系统变量(global)
②会话系统变量(session)
全局系统变量:更改全局系统变量,所有会话的全局系统变量都改变。针对于所有会话(连接)有效,但不能跨重启。
会话系统变量:更改当前会话的会话系统变量,对其他会话不造成影响,仅针对于当前会话(连接)有效。

2.2查看系统变量

如果没有指明是查看global变量还是session变量,则默认为查看session变量。
#查看所有全局/会话系统变量

SHOW GLOBAL/SESSION VARIABLES;

#查看满足条件的部分全局/会话系统变量

SHOW GLOABLE/SESSION VARIABLES LIKE '%标识符%';

查看指定系统变量:作为编码规范,Mysql中用两个‘@’符号标记系统变量。(@global,@session)
(如果写的是"@@变量名",则先在会话系统变量中找,会话系统变量中不存在,则在全局系统变量中找)
#查看指定全局/会话系统变量
SELECT @@global./@@session.变量名;
SELECT @@变量名;

2.3修改系统变量的值

方式1.修改Mysql配置文件(该方法需要重启Mysql服务)
方式2.使用set命令重新设置系统变量的值
#为某个全局/会话系统变量赋值
#方式1:
SET @@global./@@session.变量名=变量值;

#方式2:
SET GLOBAL/SESSION 变量名=变量值;

3.用户变量:作为编码规范,Mysql中的用户变量以一个@开头。(主要用于会话用户变量)

3.1分类

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

3.2会话用户变量

变量的定义
#方式1:"=“或”:="
SET @用户变量=值;
SET @用户变量 :=值;

#方式2:":=" 或INTO 关键字
SELECT @用户变量 :=表达式[FROM等子句];
SELECT 表达式 INTO @用户变量 [FROM等子句];

3.3局部变量

定义:可以使用DECLARE语句定义一个局部变量
作用域:仅仅在定义它的BEGIN…END中有效
位置:只能放在BEGIN…END中,而且只能放在第一句

BEGIN
#声明局部变量
END

二.定义条件与处理程序

定义条件:事先定义程序执行过程中可能遇到的问题。
处理程序:在遇到问题时,应当采取的处理方式,并且保证存储过程和函数在遇到警告或错误时能继续执行。(避免程序异常停止运行)

1.#错误演示:(employees 表不允许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 ;

#调用存储过程:

CALL UpdateDataNoCondition();

@x的结果为1,因为在
UPDATE employees SET email = NULL WHERE last_name=‘Abel’;这一句出错,停止运行。
错误码为:ERROR 1048 (23000)

2.定义条件:(可选)

给Mysql中的错误码命名,有助于存储的程序代码更清晰。它将一个错误名字和指定的错误条件关联起来,该名字可以随后被用在定义处理程序的DECLARE HANDLER语句中。
定义条件使用DECLARE语句语法格式:
DECLARE 错误名字 CONDITION FOR 错误码(或错误条件)

错误码的说明:
MySQL_error_code和sqlstate_value都可以表示MySQL的错误。
例如:在ERROR 1418 (HY000)中,1418是MySQL_error_code,HY000是sqlstate_value

在1中的错误演示,可以将定义条件声明:
#方式1:使用MySQL_error_code

DECLARE Field_Not_Be_NULL CONDITION FOR 1048;

#方式2:使用sqlstate_value

DECLARE Field_Not_Be_NULL CONDITION FOR SQLSTATE '23000';

3.定义处理程序:

语法:
DECLARE 处理方式 HANDLER FOR 错误类型处理语句

3.1处理方式:CONTINUE,EXIT,UNDO

CONTINUE:遇到错误不处理,继续执行。
EXIT:遇到错误马上退出
UNDO:遇到错误后撤回之前的操作。(MySQL中暂时不支持这样的操作。)

3.2错误类型(条件):

SQLSTATE ‘字符串错误码’:表示长度为5的sqlstate_value类型的错误代码;
MySQL_error_code:匹配数值类型错误代码
错误名称:表示DECLARE … CONDITION 定义的错误条件名称
SQLWARNING:匹配所有以01开头的SQLSTATE错误代码
NOT FOUND:匹配所有以02开头的SQLSTATE错误代码
SQLEXCEPTION:匹配所有没有被SQLWARNING或NOT FOUND 捕获的SQLSTATE错误代码

3.3处理语句:"SELECT 变量 = 值"或"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;

#举例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';

解决上一个案例:

CREATE PROCEDURE UpdateDataNoCondition()
BEGIN

#声明处理程序
#处理方式1:

DECLARE CONTINUE HANDLER FOR 1048 SET @prc_value =-1;

#处理方式2:

DECLARE CONTINUE HANDLER FOR SQLSTATE '23000' SET @prc_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 ;

三.流程控制

一.分类

1.顺序结构:
2.分支结构:
3.循环结构:

针对于MySQL的流程控制语句主要有3类。(tips:只能用于存储过程)
1.条件判断语句:IF语句和CASE语句
2.循环语句:LOOP,WHILE和TREPEAT语句
3.跳转语句:ITERATE和LEAVE语句

二.分支结构

1.IF分支结构

语法:
IF 表达式1 THEN 操作1
[ELSEIF 表达式2 THEN 操作2]…
[ELSE 操作N]
END IF

#案例1:

DELIMITER //
CREATE PROCEDURE test_if()
BEGIN
#声明局部变量
DECLARE stu_name VARCHAR(15);
IF stu_name IS NULL
THEN SELECT 'stu_name is null';
ELSE 
SELECT 'email is not null';
END IF;
END //
DELIMITER;

#案例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 emp_hire_date DATE;#入职时间

#赋值
SELECT salary INTO emp_sal FROM employees WHERE employee_id=emp_id;
SELECT DATEDIFF(CURDATE(),hire_date)/365 hire_year INTO emp_hire_date FROM employees WHERE employee_id=emp_id;
#DATEDIFF()计算两个时间相差的天数

#判断
IF emp_sal <8000 AND hire_year>5
THEN UPDATE employees SET salary =salary+500 where employee_id =emp_id;
END IF;
END //
DELIMITER;

2.CASE分支结构

CASE语法结构1:
#情况1:类似于switch
CASE 表达式
WHEN 值1 THEN 结果1或语句1(是语句,则要加分号)
WHEN 值2 THEN 结果2或语句2(如果是语句,需要加分号)

ELSE 结果n或语句n(如果是语句,需要加分号)
END【CASE】(如果是放在BEGIN END 中需要加上CASE,如果放在SELECT语句后面则不需要)

CASE语句的语法结构2:
#情况2:类似于多重if语句
CASE
WHEN 条件1 THEN 结果1或语句1(如果是语句,则需要加分号)
WHEN 条件2 THEN 结果2或语句2(如果是语句,则需要加分号)

ELSE 结果n或语句n(如果是语句,则需要加分号)
END【CASE】(如果是放在BEGIN END 中需要加上CASE,如果放在SELECT语句后面则不需要)

#案例1:

DELIMITER //
CREATE PROCEDURE test_case()
BEGIN
#演示1:case…when…when
DECLARE var INT DEFAULT 2;

CASE var
WHEN 1 THEN SELECT 'var = 1';
WHEN 2 THEN SELECT 'var = 2';
WHEN 3 THEN SELECT 'var = 3';
ELSE SELECT 'other value';
END CASE;

#演示2:case when…when…

DECLARE var INT DEFAULT 10;
CASE 
WHEN var>100 THEN SELECT '三位数';
WHEN var>=10 THEN SELECT '两位数';
ELSE SELECT '个位数';
END CASE;

#案例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 employee_id =emp_id;
SELECT commission_pct INTO bonus FROM employees WHERE employee_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 //
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 date INT;#记录员工入职时间
#变量赋值
SELECT ROUND(DATEDIFF(CURDATE(),hire_date)/365) INTO date FROM employees WHERE employee_id =emp_id;

CASE date
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 //
DELIMITER;

三.循环结构

1.循环结构之LOOP

Loop内的语句一直重复执行直到循环被退出(使用LEAVE子句),跳出循环过程。
Loop循环语法:
【loop——label:】LOOP
循环体
END LOOP【loop_label】

#案例1:

DELIMITER //
CREATE PROCEDURE test_loop()
BEGIN
#声明局部变量
DECLARE num INT DEFAULT 1;
loop_label:LOOP
#重新赋值
SET num=num+1;
IF num>=10 THEN LEAVE loop_label;
END LOOP loop_label;
#查看num
SELECT num;
END //
DELIMITER;

#案例2:涨工资。声明存储过程“update_salary_loop()”,声明OUT参数num,输出循环的次数。存储过程中实现循环进行涨薪,薪资涨为原来的1.1倍,直到全公司平均薪资达到12000结束。
并统计循环次数。

DELIMITER //
CREATE PROCEDURE update_salary_loop(OUT num INT)
BEGIN
#声明变量
DECLARE avg_sal DOUBLE;#记录员工的平均工资
DELCARE loop_count INT DEFAULT 0;#记录循环的次数
#获取员工的平均工资
SELECT AVG(salary) INTO avg_sal FROM employees;
loop_label:LOOP
#结束循环的条件
IF avg_sal>12000 
THEN LEAVE loop_label;
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_label;
SET num=loop_count;
END //
DELIMITER;

2.循环结构之WHILE

WHILE循环语法:
【while_label:】WHILE 循环条件 DO
循环体
END WHILE 【while_label】;

/*
凡是循环结构,一定具备4个要素:
1.初始化条件
2.循环条件
3.循环体
4.迭代条件

*/

#案例1:

DELIMITER //
CREATE PROCEDURE test_while()
BEGIN 
	#初始化条件
	DECLARE num INT DEFAULT 1;
  #循环条件
  WHILE num <=10 DO
  	#循环体
    
    #迭代条件
    SET num=num+1;
  END WHILE;
  SELECT num;
END //
DELIMITER;

#案例2:降薪。声明存储过程“update_salary_while()”,声明OUT参数num,输出循环次数。存储过程中实现降薪为原来的90%。直到全公司的平均薪资达到5000结束。
并统计循环次数。

DELIMITER //
CREATE PROCEDURE update_salary_while(OUT num INT)
BEGIN
	#声明变量
	DECLARE avg_sal DOUBLE;#记录平均工资
  DECLARE while_count INT DEFAULT 0;#记录循环次数
  
  #赋值
  SELECT AVG(salary) INTO avg_sal FROM employees;
  
  WHILE avg_sal>5000 DO
  	UPDATE employees SET salary=salary*0.9;
    SELECT AVG(salary) INTO avg_sal FROM employees;
    while_count=while_count+1;
  END WHILE;
  SET num=while_count;
END //
DELIMITER;

3.循环结构之REPEAT

REPEAT循环语法:
【repeat_label:】REPEAT
循环体
UNTIL 循环结束的条件表达式(为真则退出循环)UNTIL语句后不能加分号!!!
END REPEAT 【repeat_label】

#举例1:

DELIMITER //
CREATE PROCEDURE test_repeat()
BEGIN
	#声明变量
  DECLARE num INT DEFAULT 1;
  REPEAT
  	SET num=num+1;
    UNTIL num >=10 #不能加分号!!!!
  END REPEAT;
  
  SELECT num;
END //

#举例2:涨薪。声明存储过程“update_salary_repeat()”,声明OUT参数num,输出循环次数。涨薪为原来的1.15倍。直到全公司平均工资为13000结束。
并统计循环次数。

DELIMITER //
CREATE PROCEDURE update_salary_repeat(OUT num INT)
BEGIN
	#声明变量
	DECLARE avg_sal DOUBLE;#记录平均工资
  DECLARE repeat_count INT DEFAULT 0;#记录循环次数
  
  #赋值
  SELECT AVG(salary) INTO avg_sal FROM employees;
  
  REPEAT
  	UPDATE employees SET salary=salary*1.15;
    SET repeat_count =repeat_count+1;
    SELECT AVG(salary) INTO avg_sal FROM employees;
    UNTIL avg_sal>=13000
  END REPEAT;
  SET num=repeat_count;
END //
DELIMITER;

4.对比三种循环结构

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

四.跳转语句

1.LEAVE语句

语法:
LEAVE 标记名

#举例1:创建存储过程“leave_begin()”,声明INT类型的IN参数num。给BEGIN…END加标记名
如果num<=0,则使用LEAVE语句退出BEGIN…END
如果num=1,则查询employees中的平均工资
如果num=2,则查询employees中的最低工资
如果num>2,则查询employees中的最高工资
IF语句后结束后查询employees表中的总人数。

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;
  ELSEIF num>2 THEN 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;#记录循环次数
  SELEC AVG(salary) INTO avg_sal FROM employees;
  
  while_label:WHILE TRUE DO
  	IF avg_sal<=10000 
    	THEN LEAVE 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;

2.ITERATE语句:只能在循环语句中使用,表示重新开始循环(类似于java中的continue)

语法:
ITERATE label

#举例1:定义局部变量num,初始值为0。循环体中执行num+1操作。
如果num<10,则继续执行。
如果num>15,则退出循环结构。

DELIMITER //
CREATE PROCEDURE test_iterate()
BEGIN
	DECLARE num INT DEFAULT 0;
  loop_label:LOOP:
  	#赋值
    SET num=num+1;
    IF num<10 
    	THEN ITERATE loop_label;
    ELSEIF num>15
    	THEN LEAVE loop_label;
    END IF;
  END LOOP;
END //
DELIMITER;

四.游标

一.什么是游标

游标充当了MySQL中的指针操作,可以针对于某一条记录进行修改。

二.使用游标的步骤

1.声明游标(要放在声明变量的后面)

语法1:(适用于MySQL,SQL server,DB2和MariaDB)
DECLARE cursor_name CURSOR FOR select_statement;(select_statement)代表select语句,这里还没有写
语法2:(适用于Oracle或者PostgreSQL)
DECLARE cursor_name CURSOR IS select_statement;

2.打开游标

OPEN cursor_name;

3.使用游标

语法:
FETCH cursor_name INTO var_name 【,var_name】…(使用cursor_name来读取当前行,并保存到var_name这个变量中。

tips:1.var_name必须在使用之前定义好
2.游标的查询结果集中的字段数必须和INTO后面的变量数一致

4.关闭游标

语法:
CLOSE cursor_name;

三.游标的使用

①声明游标
②打开游标
③使用游标
④关闭游标

#案例:创建存储过程“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;#记录累加的人数
  
  #声明游标
  DECLARE emp_cursor CURSOR FOR SELECT salary FROM employees ORDER BY salary DESC;
  #打开游标
  OPEN emp_cursor;
  #使用游标
  REPEAT
  	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;
  #关闭游标
  CLOSE emp_cursor;
END //
DELIMITER;

四.小结

优点:为逐条读取结果集中的数据提供了完美的方案。
缺点:在使用游标的过程中,会对数据进行加锁,在并发量大的时候会影响业务的效率,还会消耗系统资源,造成内存不足。

MySQL8.0新特性
SET PERSIST global max_connections=1000;
global关键字加上PERSIST之后全局变量可以跨重启。

创作不易,喜欢我的文章的话记得点赞收藏加关注哦~

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

漩涡编程,一鸣惊人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值