Jw
首页
技术
英语
首页
技术
英语
  • MySQL

    • MySQL 基础
    • 环境变量配置(PATH)
    • SQL 语法与分类
    • MySQL 数据类型
    • SQL - DDL 数据定义语言
    • SQL - DML 数据操作语言
    • SQL - DQL 数据查询语言
    • SQL - DCL 数据控制语言
    • MySQL 函数
    • SQL 约束
    • SQL 外键约束
    • 多表关系
    • 多表查询(连接查询)
    • 隐式内连接 vs 显式内连接
    • 子查询
    • 多表查询案例
    • 事务
    • 并发事务问题与隔离级别
    • MySQL 体系结构与存储引擎
    • 索引概述
    • 索引结构
    • 索引分类
    • 索引语法
    • SQL 性能分析
    • 索引使用
    • 索引设计原则
    • SQL 优化 - 插入数据与主键优化
    • SQL 优化 - order by 与 group by
    • SQL 优化 - limit / count / update
    • 视图
    • 存储过程
    • 存储过程 - 变量
    • 存储过程 - 流程控制、游标与条件处理
    • 存储函数
    • 触发器
    • 锁概述与全局锁
    • 表级锁
    • 行级锁
    • InnoDB 逻辑存储结构
    • InnoDB 架构
    • 事务原理(redo / undo log)
    • MVCC
    • MySQL 系统数据库
    • MySQL 常用工具
    • 数据备份与导入导出
    • 错误日志与二进制日志
    • 查询日志与慢查询日志
    • 主从复制 - 概述与原理
    • 主从复制 - 搭建
    • Mycat 配置
    • Mycat 垂直拆分与水平拆分
    • Mycat 分片规则
    • Mycat 管理与监控
    • Mycat 读写分离 - 一主一从
    • Mycat 读写分离 - 双主双从

存储过程 - 流程控制、游标与条件处理

流程控制(IF / CASE / WHILE / REPEAT / LOOP)让存储过程能分支与循环;游标用来逐行处理查询结果,**条件处理程序(Handler)**用来处理游标或异常等情况。

一、IF 条件判断

IF 条件1 THEN
    ...
ELSEIF 条件2 THEN      -- 可选
    ...
ELSE                   -- 可选
    ...
END IF;

示例:根据局部变量 score 判定等级(≥85 优秀;≥60 且 <85 及格;<60 不及格):

CREATE PROCEDURE p3()
BEGIN
    DECLARE score INT DEFAULT 85;
    DECLARE score_txt VARCHAR(10);
    IF score >= 85 THEN
        SET score_txt := '优秀';
    ELSEIF score >= 60 THEN
        SET score_txt := '及格';
    ELSE
        SET score_txt := '不及格';
    END IF;
    SELECT score_txt;
END;

CALL p3();

二、CASE 多分支

语法一:等值匹配

CASE case_value
    WHEN when_value1 THEN statement_list1
    [WHEN when_value2 THEN statement_list2] ...
    [ELSE statement_list]
END CASE;

语法二:任意条件匹配

CASE
    WHEN search_condition1 THEN statement_list1
    [WHEN search_condition2 THEN statement_list2] ...
    [ELSE statement_list]
END CASE;

示例:根据传入的月份(IN)判定季度(OUT),使用语法一:

CREATE PROCEDURE p6(IN month INT, OUT season VARCHAR(10))
BEGIN
    CASE
        WHEN month IN (1, 2, 3)    THEN SET season := '第一季度';
        WHEN month IN (4, 5, 6)    THEN SET season := '第二季度';
        WHEN month IN (7, 8, 9)    THEN SET season := '第三季度';
        WHEN month IN (10, 11, 12) THEN SET season := '第四季度';
        ELSE SET season := '非法参数';
    END CASE;
END;

CALL p6(4, @season);
SELECT @season;          -- 第二季度

上例 CASE 后没有写 case_value,属于语法二的写法(以 WHEN 条件 判断)。若写成 CASE month WHEN ...,则为语法一。

三、循环

3.1 WHILE:先判断,后执行

WHILE 条件 DO
    -- SQL 逻辑
END WHILE;
-- 计算从 1 累加到 n
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;

CALL p7(10);             -- 55

3.2 REPEAT:先执行,后判断

REPEAT
    -- SQL 逻辑
UNTIL 条件
END REPEAT;

注意:UNTIL 的条件为真时才退出,与 WHILE 的条件相反。

CREATE PROCEDURE p8(IN n INT)
BEGIN
    DECLARE total INT DEFAULT 0;
    REPEAT
        SET total := total + n;
        SET n := n - 1;
    UNTIL n <= 0
    END REPEAT;
    SELECT total;
END;

CALL p8(10000);          -- 50005000

3.3 LOOP:配合 LEAVE / ITERATE

LOOP 本身是死循环,需要配合两个语句控制:

语句作用
LEAVE 标签;退出指定标记的循环(类似 break)
ITERATE 标签;跳过本次循环剩下的语句,直接进入下一次循环(类似 continue),只能用在循环中
[begin_label:] LOOP
    -- SQL 逻辑
END LOOP [end_label];
-- 计算从 1 累加到 n
CREATE PROCEDURE p9(IN n INT)
BEGIN
    DECLARE total INT DEFAULT 0;
    sum: LOOP
        IF n <= 0 THEN
            LEAVE sum;
        END IF;
        SET total := total + n;
        SET n := n - 1;
    END LOOP sum;
    SELECT total;
END;

CALL p9(100);            -- 5050

-- 计算从 1 到 n 之间的偶数累加(奇数直接跳过)
CREATE PROCEDURE p10(IN n INT)
BEGIN
    DECLARE total INT DEFAULT 0;
    sum: LOOP
        IF n <= 0 THEN
            LEAVE sum;
        ELSEIF n % 2 != 0 THEN
            SET n := n - 1;
            ITERATE sum;
        END IF;
        SET total := total + n;
        SET n := n - 1;
    END LOOP sum;
    SELECT total;
END;

CALL p10(10);            -- 30(10+8+6+4+2)

四、游标(CURSOR)

游标用来存储查询结果集,在存储过程 / 函数中可以对结果集逐行循环处理。使用流程:声明 → 打开 → 获取 → 关闭。

步骤语法
声明游标DECLARE 游标名称 CURSOR FOR 查询语句;
打开游标OPEN 游标名称;
获取记录FETCH 游标名称 INTO 变量 [, 变量] ...;
关闭游标CLOSE 游标名称;

示例:查询 tb_user 中年龄小于等于 uage 的用户的姓名(name)和专业(profession),插入新表 tb_user_pro(id, name, profession):

CREATE PROCEDURE p11(IN uage INT)
BEGIN
    DECLARE uname VARCHAR(100);
    DECLARE upro VARCHAR(100);
    DECLARE u_cursor CURSOR FOR SELECT name, profession FROM tb_user WHERE age <= uage;

    DROP TABLE IF EXISTS tb_user_pro;
    CREATE TABLE IF NOT EXISTS tb_user_pro(
        id INT PRIMARY KEY AUTO_INCREMENT,
        name VARCHAR(100),
        profession VARCHAR(100)
    );

    OPEN u_cursor;
    WHILE true DO
        FETCH u_cursor INTO uname, upro;
        INSERT INTO tb_user_pro VALUES (NULL, uname, upro);
    END WHILE;
    CLOSE u_cursor;
END;

执行 CALL p11(40); 时,游标取完最后一行后再 FETCH 会产生 02000(NOT FOUND)警告,程序会因此报 No data - zero rows fetched... 并中断,下一节的 Handler 就是用来处理它的。

五、条件处理程序(HANDLER)

条件处理程序用于定义流程控制中遇到问题时的处理步骤。

DECLARE handler_action HANDLER FOR condition_value [, condition_value] ... statement;
项取值含义
handler_actionCONTINUE继续执行当前程序
EXIT终止执行当前程序(所在的 BEGIN ... END 块)
condition_valueSQLSTATE 'sqlstate_value'指定状态码,如 '02000'
SQLWARNING所有以 01 开头的 SQLSTATE 的简写
NOT FOUND所有以 02 开头的 SQLSTATE 的简写(游标无数据)
SQLEXCEPTION所有未被 SQLWARNING 或 NOT FOUND 捕获的 SQLSTATE 的简写

示例:在 p11 中声明 EXIT Handler,游标无数据时关闭游标并退出:

CREATE PROCEDURE p11(IN uage INT)
BEGIN
    DECLARE uname VARCHAR(100);
    DECLARE upro VARCHAR(100);
    DECLARE u_cursor CURSOR FOR SELECT name, profession FROM tb_user WHERE age <= uage;
    DECLARE EXIT HANDLER FOR SQLSTATE '02000' CLOSE u_cursor;

    DROP TABLE IF EXISTS tb_user_pro;
    CREATE TABLE IF NOT EXISTS tb_user_pro(
        id INT PRIMARY KEY AUTO_INCREMENT,
        name VARCHAR(100),
        profession VARCHAR(100)
    );

    OPEN u_cursor;
    WHILE true DO
        FETCH u_cursor INTO uname, upro;
        INSERT INTO tb_user_pro VALUES (NULL, uname, upro);
    END WHILE;
    CLOSE u_cursor;
END;

CALL p11(30);

也可以写成 DECLARE EXIT HANDLER FOR NOT FOUND CLOSE u_cursor;,效果相同。

顺序易错:存储过程块内的声明必须按 变量 → 游标 → Handler 的顺序写在最前面,否则会报语法错误。

速查

-- IF:多分支
IF score >= 85 THEN SET r := '优秀';
ELSEIF score >= 60 THEN SET r := '及格';
ELSE SET r := '不及格';
END IF;

-- WHILE:先判断再执行(条件为真才进入)
WHILE n > 0 DO SET n := n - 1; END WHILE;

-- REPEAT:先执行再判断(UNTIL 条件为真时退出)
REPEAT SET n := n - 1; UNTIL n <= 0 END REPEAT;

-- LOOP:LEAVE 退出,ITERATE 跳过本次
sum: LOOP
    IF n <= 0 THEN LEAVE sum; END IF;
    SET n := n - 1;
END LOOP sum;

-- 游标:声明 → 打开 → 取值 → 关闭(需配合 Handler 处理 02000)
DECLARE u_cursor CURSOR FOR SELECT name FROM tb_user;
DECLARE EXIT HANDLER FOR SQLSTATE '02000' CLOSE u_cursor;
OPEN u_cursor;
FETCH u_cursor INTO uname;
CLOSE u_cursor;
要做什么用什么
分支IF;等值或区间多选用 CASE
循环WHILE 先判断,REPEAT 先执行
跳出 / 跳过LEAVE(退出)、ITERATE(下一次)
逐行处理结果游标 + EXIT HANDLER FOR SQLSTATE '02000'

相关

  • 存储过程:创建、调用、参数(IN / OUT / INOUT)
  • 存储过程:变量:局部变量的 DECLARE 与赋值
  • SQL DQL 数据查询语言:游标所包装的 SELECT 查询
最近更新: 2026/10/13 21:49
Contributors: liujw
Prev
存储过程 - 变量
Next
存储函数