MySQL---变量、流程控制与游标

MySQL—变量、流程控制与游标

变量

MySQL数据库的存储过程和函数中,可以使用变量来存储查询或计算的中间结果数据,或则输出最终的结果数据

在MySQL数据库中,变量分为系统变量以及用户自定义变量

系统变量

  • 变量由系统定义,不是用户定义,属于服务器层面
  • 系统变量定义了当前MySQL服务实例的属性、特征。
  • 这些系统变量的值要么是编译MySQL时参数的默认值,要么是配置文件中的参数值
  • 系统变量分为全局系统变量(global)和会话系统变量(session)。如果不写,则默认会话级别。静态变量属于特殊的全局系统变量

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

  • 全局系统变量针对于所有会话(连接)有效,但不能跨重启
  • 会话系统变量仅针对于当前会话(连接)有效。会话期间,当前会话对某个会话系统变量值的修改,不会影响其他会话同一个会话系统变量的值
  • 会话1对某个全局系统变量值的修改会导致会话2中同一个全局系统变量值的修改

查看系统变量

SHOW GLOBAL VARIABLES;

SHOW SESSION VARIABLES;

SHOW VARIABLES;

SHOW GLOBAL VARIABLES LIKE 'admin%';

SHOW VARIABLES LIKE 'character_%';

查看指定系统变量

SELECT @@global.MAX_CONNECTIONS;

SELECT @@session.character_set_client;
#先查找会话系统变量再查找全局系统变量
SELECT @@character_set_client;

修改系统变量的值

针对当前数据库的实例有效,但一旦重启MySQL服务,就失效了
SET @@global.MAX_CONNECTIONS=161;
SET GLOBAL MAX_CONNECTIONS=171;

针对当前会话是有效的的,一旦结束会话,重新建立起新的会话,就失效了
SET @@session.character_set_client='gbk';
SET SESSION character_set_client='gbk';

用户变量

用户变量是由用户自己定义的。MySQL中的用户变量以一个@开头。根据作用范围不同,可分成会话用户变量和局部变量

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

会话用户变量

SET @m1 = 1;
SET @m2 := 2;
SET @sum := @m1+@m2;
SELECT @sum;
SELECT @count:= COUNT(*) FROM emp;

SELECT @count;

SELECT AVG(salary) INTO @avg_sal
FROM emp;

SELECT @avg_sal;

局部变量

定义:使用DECLEAR语句定义一个局部变量

作用域:仅仅在定义它的BEGIN…END中有效(使用在存储过程和存储函数中)

位置:只能放在BEGIN…END中,并且只能放在第一句

DECLEAR 变量名 类型【default 值】;如果没有DEFAULT子句,初始值为NULL


DELIMITER //
CREATE PROCEDURE test_val()
BEGIN 

	DECLARE a INT DEFAULT 0;
	DECLARE b INT;
	DECLARE emp_name VARCHAR(25);

	SET a =1;
	SET b:=2;
	SELECT last_name INTO emp_name 
	FROM emp WHERE employee_id=101;
	
	SELECT a,b,emp_name;

END //

DELIMITER ;

CALL test_val();
作用域 定义位置 语法
用户会话变量 当前会话 会话的任意的地方 加@符号,不用指定类型
局部变量 定义它的BEGIN…END中 BEGIN…END的第一句化 一般不加@,需要指定而类型

定义条件与处理程序

定义条件:事先定义程序执行过程中可能遇到的问题

处理程序:定义了在遇到问题时应当采取的处理方式,并保证存储过程或函数在遇到警告或错误时能继续执行

定义条件

定义条件:就是给MySQL中的错误码命名,有助于存储的程序代码更轻西。其将一个错误名字和指定的错误条件关联起来,这个名字可以随后被用在定义处理程序的DECLEAR HANDLER语句中

DECLARE 错误名称 CONDITION FOR 错误码(错误条件)

错误码:

  • MySQL_error_code 数值类型错误代码
  • sqlstate_value 长度为5的字符串类型错误代码
DECLARE Fileld_NOT_BE_NULL CONDITION FOR 1048;
DECLARE Fileld_NOT_BE_NULL CONDITION FOR SQLSATATE '23000';

定义处理程序

可以为SQL职系那个过程中发生的某种类型的错误定义特殊的处理程序

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

处理方式

  • CONTINUE:遇到错误不处理,继续执行
  • EXIT:遇到错误马上退出
  • UNDOO:遇到错误后撤回之前的操作,MySQL暂不支持

错误类型

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

处理语句

  • SET 变量=‘值’
  • BEGIN…END 编写的复合语句
DECLARE CONTINUE HANDLER FOR SQLSTATE '42s02' SET @info='NO_SUCH_TABLE';

声明处理程序需要在BEGIN中进行处理程序

流程控制

流程控制语句的作用控制存储过程中SQL语句的执行顺序。根据执行的程序,流程可以分为

  • 顺序结构:程序从上往下依次执行
  • 分支结构:程序按条件进行选择执行,从两条或多条路径中选择一条进行执行
  • 循环结构:程序满足一定条件下,重复执行一组语句

针对三种不同的MySQL流程控制语句分为3类

  • 条件判断语句:IF 和 CASE
  • 循环语句: LOOP WHILE REPET
  • 跳转语句: ITERATE 和 LEAVE 语句

分支结构—IF

IF 表达式1 THEN 操作1
[ELSEIF 表达式2 THEN 操作2]...
[ELSE 操作N]
END IF
  • 不同的表达式对应不同操作
  • 在BEGIN … END 中使用
DELIMITER //

CREATE PROCEDURE test_if()
BEGIN 

	DECLARE stu_name VARCHAR(20);
	IF stu_name IS NULL
		THEN SELECT 'stu_name is NULL';
	END IF;

END //

DELIMITER ;

CALL test_if();
DROP PROCEDURE test_if;

分支结构— CASE

CASE 语法结构

CASE 表达式
WHEN1 THEN 结果1或语句1
WHEN2 THEN 结果2或语句2
...
ELSE 结果n或语句n
END [case] 如果放在bigin end中需要加上case,如果放在select后面则不需要
CASE
WHEN 条件1 THEN 结果1或语句1
WHEN 条件2 THEN 结果2或语句2
...
ELSE 结果n或语句n
END [case] 如果放在bigin end中需要加上case,如果放在select后面则不需要 


DELIMITER //
CREATE PROCEDURE test_case()
BEGIN 

	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;

END //
DELIMITER ;

CALL test_case();
DELIMITER //
CREATE PROCEDURE test_case2()
BEGIN 

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

END //
DELIMITER ;

CALL test_case2();

循环结构—LOOP

LOOP循环语句用来重复执行某些语句。LOOP内的语句一直重复执行直到循环被退出(使用LEAVE子句),跳出循环的过程

[loop_label:] LOOP
循环执行的语句
END LOOP[loop_label]

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 IF;
END LOOP loop_label;

SELECT num;

END //


DELIMITER ;
CALL test_loop();

loop_label可以省略

循环结构—while

while语句创建一个带条件判断的循环过程。WHILE在执行语句时,先对指定的表达式进行判断,如果为真,就执行循环内的语句,否则退出循环。

[while_label] WHILE 循环条件 DO
循环体
END WHILE[while_label];

凡是循环结构,一定具有四个要素

  • 初始化条件
  • 循环条件
  • 循环体
  • 迭代条件
DELIMITER //
CREATE PROCEDURE test_while()

BEGIN

DECLARE num INT DEFAULT 1;

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

SELECT num;

END //

CALL test_while();

循环结构—REPEAT

REPEAT创建一个带条件判断的循环过程。与WHILE循环不同,REPEAT循环首先执行一次循环,然后在UNTIL中进行表达式判断,如果满足条件就退出,即END REPEAT;如果条件不满足,则会继续执行循环,知道满足退出条件为止

[repeat_label:]REPEAT
循环体的语句
UNTIL 结束循环的条件表达式
END REPEAT [repeat_label]
DELIMITER //
CREATE PROCEDURE test_repeat()

BEGIN

	DECLARE num INT DEFAULT 1;
	REPEAT 
		SET num = num+1;
		UNTIL num >= 10
	END REPEAT;

	SELECT num;

END //
DELIMITER ;

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

跳转语句—LEAVE语句

LEAVE语句:可以在循环语句中,或则以BEGIN和END包裹起来的程序体内,表示跳出循环或则跳出程序体的操作,相当于break

LEAVE 标记名
DELIMITER //
CREATE PROCEDURE test_leave(IN num INT)

begin_label:BEGIN

	IF num<=0
		THEN LEAVE begin_label;
	ELSEIF num=1
		THEN SELECT AVG(salary) FROM emp;
	ELSEIF num=2
		THEN SELECT MIN(salary) FROM emp;
	ELSE
		SELECT MAX(salary) FROM emp;
	END IF;
	SELECT COUNT(*) FROM emp;
END //
DELIMITER ;

call test_leave(3);

跳转语句—ITERATE语句

只能在循环语句(LOOP、REPEAT和WHILE语句)内,表示重新开始循环,将执行顺序转到语句段开头处,相当于continue

ITERATE label
DELIMITER //
CREATE PROCEDURE test_iterate ()

begin_label: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;
			
			SELECT num;
	
	END LOOP;
END //
DELIMITER ;

CALL test_iterate();

游标

游标:提供了一种灵活的操作方式,可以对集合中的每一条记录进行定位,并对指定的记录中的数据进行操作的数据结构

游标让SQL这种面向集合的语言有了面向过程开发的能力

游标充当了指针的作用。在SQL中,游标是一种临时的数据库对象,可以指向存储在数据库表中的数据行指针,可以通过操作游标来对数据进行操作

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

使用游标的步骤

  • 创建游标

需要放在变量声明的下面

DECLARE cursor_name CURSOR FOR selete_statement
DECLARE cursor_name CURSOR IS selete_statement
  • 打开游标
OPEN cursor_name
  • 使用游标(从游标中取得数据)
FETCH cursor_name INTO var_name1,...,var_namen

游标的查询结果集中的字段数,必须跟INTO后面的变量数一致,否则存储过程执行的时候,MySQL会提示错误

  • 关闭游标
CLOSE cursor_name

因为游标会占用系统资源,如果不及时关闭,游标会一直保持到存储过程结束,影响系统运行的效率


DELIMITER //
CREATE PROCEDURE get_count_by_limit_total_salary(IN limit_today_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 emp 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_today_salary
	END REPEAT;

	SET total_count=emp_count;
	CLOSE emp_cursor;
END //
DELIMITER ;

CALL get_count_by_limit_total_salary(200000,@total_count);
SELECT @total_count;
  • 游标为逐条读取结果集中的数据,提供了完美的解决方案
    sum_sal DOUBLE DEFAULT 0.0;
    DECLARE emp_sal DOUBLE;
    DECLARE emp_count INT DEFAULT 0;

    DECLARE emp_cursor CURSOR FOR SELECT salary FROM emp 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_today_salary
    END REPEAT;

    SET total_count=emp_count;
    CLOSE emp_cursor;
    END //
    DELIMITER ;

CALL get_count_by_limit_total_salary(200000,@total_count);
SELECT @total_count;


- 游标为逐条读取结果集中的数据,提供了完美的解决方案
- 但会对数据进行加锁,并在业务并发量大的时候,不仅会影响业务之间的效率,还会消耗系统资源,造成内存不足

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