MySQL数据库精研之旅第十九期:存储过程,数据处理的全能工具箱(二)

专栏:MySQL数据库成长记

个人主页:手握风云

目录

[一、SQL 编程](#一、SQL 编程)

[1.1. CASE](#1.1. CASE)

[1. 语法1](#1. 语法1)

[2. 语法2](#2. 语法2)

[3. 示例](#3. 示例)

[1.2. WHILE 循环](#1.2. WHILE 循环)

[1. 语法](#1. 语法)

[2. 示例](#2. 示例)

[1.3. REPEAT 循环](#1.3. REPEAT 循环)

[1. 语法](#1. 语法)

[2. 示例](#2. 示例)

[1.4. LOOP 循环](#1.4. LOOP 循环)

[1. 语法](#1. 语法)

[2. 示例](#2. 示例)

[1.5. 游标](#1.5. 游标)

[1. 语法](#1. 语法)

[2. 示例](#2. 示例)

[1.6. 条件处理程序](#1.6. 条件处理程序)

[1. 定义](#1. 定义)

[2. 示例](#2. 示例)


一、SQL 编程

1.1. CASE

1. 语法1

  • CASE 后的 case_value是一个表达式,该表达式的值与每一个 WHEN 子句中的 when_value 比较,当找到一个相等的 when value 时,执行相应的 THEN 子句的 statement list 。
  • 如果没有相等的 when value,则执行 ELSE 子句 statement list(如果存在ELSE)。
sql 复制代码
CASE case_value
    WHEN when_value THEN statement_list
    [WHEN when_value THEN statement_list] ...
    [ELSE statement_list]
END CASE

2. 语法2

  • 计算每个 WHEN 子句 search_condition 表达式,直到其中一个表达式为真,此时执行相应的 THEN 子句的 statement list。
  • 如果 search_condition 都不相等,则执行 ELSE 子句 statement_list(如果存在ELSE)。
sql 复制代码
CASE
    WHEN search_condition THEN statement_list
    [WHEN search_condition THEN statement_list] ...
    [ELSE statement_list]
END CASE

注意:每个 statement_list 由一条或多条 SQL 语句组成且不允许为空。但可以使用 BEGIN... END; 块;如果没有 when_value 或 search _condition 与测试值匹配,并照 CASE 语句不包含 ELSE 子句,则会导致 CASE 语句错误。

3. 示例

传⼊⼀个状态码,输出该状态码表示的含义。0表示成功;10001表示⽤户名或密码错误;10002,您没有对应的权限,请联系管理员;20001,你传入的参数有误;20002,没有找到相应的结果。

sql 复制代码
delimiter //
CREATE PROCEDURE p5 (IN code INT, OUT ret VARCHAR(50))
BEGIN
CASE code
    WHEN 0 THEN
        SET ret := '成功';
    WHEN 10001 THEN
        SET ret := '用户名或密码错误';
    WHEN 10002 THEN
        SET ret := '你没有对应的权限';
    WHEN 20001 THEN
        SET ret := '你传入的参数有误';
    WHEN 20002 THEN
        SET ret := '没有找到相应的结果';
    ELSE
        SET ret := '服务器错误,请联系管理员';
END CASE;
END//
delimiter ;

-- 调用
CALL p5(10001, @ret);
-- 查看结果
SELECT @ret;

根据传入的月份,输出该月份属于哪个季度。1~3月为第⼀季度;4~6月为第二季度;7~9月为第三季度;10~12月为第四季度。

sql 复制代码
delimiter //
CREATE PROCEDURE p6(IN month INT, OUT ret VARCHAR(50))
BEGIN
CASE
	WHEN month >= 1 AND month <= 3 THEN
		SET ret := '第一季度';
	WHEN month >= 4 AND month <= 6 THEN
		SET ret := '第二季度';
  WHEN month >= 7 AND month <= 9 THEN
		SET ret := '第三季度';
  WHEN month >= 10 AND month <= 12 THEN
		SET ret := '第四季度';
	ELSE
		SET ret := '非法输入';
  END CASE;
END//
delimiter ;

CALL p6(5, @ret);
SELECT @ret;

1.2. WHILE 循环

1. 语法

先判断条件表达式 search_condition 是否为 TRUE,如果条件成立,则执行循环体中的 statement list。

sql 复制代码
WHILE search_condition DO
    statement_list
END WHILE;

2. 示例

传入⼀个数 n,计算从1累加到 n 的值。

sql 复制代码
delimiter //
CREATE PROCEDURE p7 (IN n INT)
BEGIN
  -- 定义变量保存结果
  DECLARE
  total INT DEFAULT 0;
  -- 循环
  WHILE n > 0 DO
    -- 累加
    SET total := total + n;
    SET n := n - 1;
  END WHILE;
  SELECT total;
END //
delimiter;

CALL p7(100);

1.3. REPEAT 循环

1. 语法

  • 先执行一次循环体中的 statement 1ist,再先判断条件表达式 search_condition 是否为 TRUE,如果条件成立,则继续执行循环体中的语句,如果条件不成立则退出循环。
  • statement list 至少会执行一次,类似与 JAVA 中的do ... while 循环。
sql 复制代码
REPEAT
    statement_list
UNTIL search_condition
END REPEAT;

2. 示例

传入⼀个数n,计算从1累加到n的值。

sql 复制代码
delimiter //
CREATE PROCEDURE p8 (IN n INT)
BEGIN
  -- 定义一个变量保存结果
  DECLARE
  total INT DEFAULT 0;
  -- 循环
  REPEAT
    SELECT
      n;
    SET total := total + n;
    SET n := n - 1;
  UNTIL n <= 0
  END REPEAT;
  -- 查看结果
  SELECT
    total;
END //
delimiter;
CALL p8 (10);

1.4. LOOP 循环

1. 语法

LOOP也可以实现一个简单的循环,并且当满足某个条件时终止当前循环或退出整个循环,通常配合以下两个子句使用。

  • LEAVE label:退出整个循环,类似于 JAVA 中的 break;
  • ITERATE label:终止当前循环,进入下一次循环,类似于 JAVA 中的 continue;
sql 复制代码
[begin_label:] LOOP
    statement_list
END LOOP [end_label]

2. 示例

sql 复制代码
delimiter //
CREATE PROCEDURE p9 (IN n INT)
BEGIN
  DECLARE
  total INT DEFAULT 0;
  sum_label :
  LOOP
    IF n <= 0 THEN
      LEAVE sum_label;
    END IF;
    SET total := total + n;
    SET n := n - 1;
  END LOOP sum_label;
  SELECT
    total;
END //
delimiter;
CALL p9 (10);

1.5. 游标

MySQL 中的游标是⼀种数据库对象,允许在存储过程和函数中对查询到的结果集进⾏逐行检索。在结果集中,可以通过向前移动游标,实现每一行的数据读取。

sql 复制代码
SELECT * from student;

注意:MySQL的游标是只读的,不能进行更新操作。

1. 语法

sql 复制代码
-- 声明游标
DECLARE 游标名 CURSOR FOR 查询语句;

-- 打开游标
OPEN 游标名;

-- 获取游标记录
FETCH 游标名 INTO 变量[, 变量] ...;

-- 关闭游标
CLOSE 游标名;

2. 示例

传入班级编号,查询学生表中属于该班级的学生信息,并将符合条件的学生信息写⼊到⼀张 新表中。我们需要先定义用于接收查询结果集中每一行列值的变量,包括存储学生姓名的变量和存储班级名的变量;然后声明游标,该游标的查询语句关联 student 表与 class 表,根据传入的班级编号筛选出符合条件的学生姓名(student_name)和班级名(class_name),用于接收筛选后的查询结果集;接着创建新表 t_student_class,表结构包含 id(自增主键)、student_name(学生姓名)、class_name(班级名);下面再执行开启游标操作,使游标可以开始获取查询结果集中的记录,并将获取到的学生姓名和班级名插入到新创建的 t_student_class 表中;最后关闭游标。

sql 复制代码
SELECT
  s.`name` student_name,
  c.`name` class_name
FROM
  student s,
  class c
WHERE
  s.class_id = c.id
  AND s.class_id = 1;
sql 复制代码
delimiter //
CREATE PROCEDURE p10 (IN class_id INT)
BEGIN
  -- 定义变量用于接收查询结果集中每一行中列的值
  -- 学生姓名
  DECLARE
  student_name VARCHAR (20);
  -- 班级名称
  DECLARE
  class_name VARCHAR (20);
  -- 声明游标
  DECLARE
  s_cursor CURSOR FOR SELECT
      s.`name` student_name,
      c.`name` class_name
    FROM
      student s,
      class c
    WHERE
      s.class_id = c.id
      AND s.class_id = 1;
    DROP TABLE
    IF EXISTS t_student_class;
      CREATE TABLE
      IF NOT EXISTS t_student_class (id BIGINT PRIMARY KEY auto_increment, student_name VARCHAR (20) NOT NULL, class_name VARCHAR (20) NOT NULL);
        
        -- 开启游标
        OPEN s_cursor;
        
        -- 遍历结果集
        WHILE TRUE DO
          -- 获取游标记录
          FETCH s_cursor INTO student_name,
          class_name;
          -- 写入到新表
          INSERT INTO t_student_class
          VALUES
          (NULL, student_name, class_name);
        END WHILE;
        -- 关闭游标
        CLOSE s_cursor;
      END //
      delimiter;

1.6. 条件处理程序

按照上面的 SQL 语句,我们调用执行一次这个函数就会出现错误。这是因为 WHILE 循环没有一个有效的终止条件,导致发生了越界。

1. 定义

定义条件是事先定义程序执行过程中可能遇到的问题,处理程序定义了在遇到问题时应当采取的处理方式,使用条件处理程序保证存储过程或函数在遇到警告或错误时能继续执⾏,可以增强程序处理问题的 能⼒,避免程序异常停止运行。

sql 复制代码
DECLARE handler_action HANDLER
    FOR condition_value [, condition_value] ...
    statement;

-- handler_action 的可选值:
--   CONTINUE  -- 继续执行当前程序
--   EXIT      -- 终止执行当前程序

-- condition_value 的可选值:
--   mysql_error_code    -- MYSQL错误码
--   SQLSTATE [VALUE] sqlstate_value  -- 状态码
--   SQLWARNING          -- 所有以01开头的SQLSTATE代码
--   NOT FOUND           -- 所有以02开头的SQLSTATE代码
--   SQLEXCEPTION        -- 所有没有被SQLWARNING或NOT FOUND捕获的SQLSTATE代码

例如,游标越界出现的状态码:

sql 复制代码
mysql> call p10(1);
ERROR 1329 (02000): No data - zero rows fetched, selected, or processed

游标必须在条件处理程序之前被声明,并且变量必须在游标或条件处理程序之前被声明。

2. 示例

加入条件处理程序,解决游标越界问题。

sql 复制代码
delimiter //
CREATE PROCEDURE p11 (IN class_id INT)
BEGIN
-- 定义变量用于接收查询结果集中每一行中列的值
-- 学生姓名
DECLARE
student_name VARCHAR (20);
-- 班级名称
DECLARE
class_name VARCHAR (20);
DECLARE is_done bool DEFAULT FALSE;
-- 声明游标
DECLARE
s_cursor CURSOR FOR SELECT
    s.`name` student_name,
    c.`name` class_name
  FROM
    student s,
    class c
  WHERE
    s.class_id = c.id
    AND s.class_id = class_id;
    
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET is_done := TRUE;
  DROP TABLE
  IF EXISTS t_student_class;
    CREATE TABLE
    IF NOT EXISTS t_student_class (id BIGINT PRIMARY KEY auto_increment, student_name VARCHAR (20) NOT NULL, class_name VARCHAR (20) NOT NULL);
      
      -- 开启游标
      OPEN s_cursor;
      
      -- 遍历结果集
      WHILE NOT is_done DO
        -- 获取游标记录
        FETCH s_cursor INTO student_name,
        class_name;
        -- 写入到新表
        INSERT INTO t_student_class
        VALUES
        (NULL, student_name, class_name);
      END WHILE;
      -- 关闭游标
      CLOSE s_cursor;
    END //
    delimiter ;
      
      CALL p11(1);
相关推荐
孟意昶2 小时前
Doris专题17- 数据导入-文件格式
大数据·数据库·分布式·sql·doris
你可以永远相信功夫熊猫2 小时前
金蝶云·星瀚 | 生产制造成本核算终极实操手册(从0到1,含两套完整案例)
数据库·erp
Thepatterraining3 小时前
MySQL零基础教程:DDL/DCL/DML详解,从建库到存储过程一篇搞定!
数据库·sql·mysql
想ai抽3 小时前
深入starrocks-怎样实现多列联合统计信息
java·数据库·数据仓库
jackletter3 小时前
待补充 五大关系数据库(sqlserver、mysql、oracle、pgsql、sqlite)的列类型:目录
mysql·oracle·sqlserver·sqlite·pgsql·列类型
Y4090013 小时前
MySQL中的“事务”
数据库·mysql
Raymond运维3 小时前
MySQL源码编译安装
linux·数据库·mysql
王木风4 小时前
什么是多版本控制(MVCC)
mysql
清风细雨_林木木4 小时前
MacOS本地数据库搭建
数据库·macos