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 ;