在 MySQL 中,循环语句是一种用于重复执行特定代码块的结构。下面将具体讨论 MySQL 存储程序语言所提供的循环处理命令,MySQL 提供了两种常用的循环语句:WHILE 循环和 LOOP 循环。
这可能是循环语句中最简单的结构,语法如下:
[label:] LOOP statements END LOOP [label];
在 LOOP 和 END LOOP 之间的语句(即 statements)将会无限循环,直到 LOOP 循环终止。你可以使用 LEAVE 语句来终止 LOOP 循环。
你可以给 LOOP 循环命名一个标签,标签可以帮助我们区分不同的 LOOP 循环语句,标签还能够用于控制执行流程。
下面示例展示了一个非常简单的循环(一个死循环,非常危险),该循环会永远运行下去,或者直到你手动终止它为止。因为存储程序是在数据库服务器内部运行的,所以需要使用 Ctrl-C 或者 KILL 命令来终止 MySQL 会话进程的方法来将其结束,或者关闭数据库服务器。
注意:循环会占用大量的 CPU 资源,不推荐你在关键任务系统中运行以下示例,否则后果很严重,请不要轻易自我尝试。
-- 定义一个循环,标签为 Infinite_loop Infinite_loop: LOOP SELECT 'Welcome to my infinite loop from hell!!'; END LOOP inifinite_loop;
LEAVE 语句允许你终止循环,LEAVE 的语法如下:
LEAVE label;
LEAVE 会使当前循环终止,标签匹配了要终止的循环,所以当一个循环被另一个循环嵌套时,我们可以使用一个语句终止所有的循环。
示例:使用执行一个 LEAVE 来退出循环,如下:
CREATE PROCEDURE demo(IN sale int) BEGIN SET sale = 1; myloop: LOOP SET sale = sale + 1; IF sale = 10 then -- 退出循环 LEAVE myloop; END IF; END LOOP myloop; SELECT concat('I can count to 10, sale=', sale) as message; END
调用存储过程:
mysql> call demo(30); +----------------------------+ | message | +----------------------------+ | I can count to 10, sale=10 | +----------------------------+ 1 row in set (0.07 sec) Query OK, 0 rows affected (0.00 sec)
ITERATE 语句用来重新从循环头部开始执行,而不执行任何在循环中遗留下来的语句,ITERATE 的语法如下:
ITERATE label;
当 MySQL 遇到 ITERATE 语句时,它会从新跳转到指定循环的头部开始执行。
示例:使用 ITERATE 打印 10 以下的奇数
CREATE PROCEDURE demo(IN sale int) BEGIN SET sale = 0; loop1: LOOP SET sale = sale + 1; IF sale >= 10 THEN -- 退出循环 LEAVE loop1; ELSEIF MOD(sale, 2) = 0 THEN -- 跳过本次循环后续语句,进入下一次循环 ITERATE loop1; END IF; SELECT CONCAT(sale, " is an odd number"); END LOOP loop1; END
调用存储过程:
mysql> call demo(30); +-----------------------------------+ | CONCAT(sale, " is an odd number") | +-----------------------------------+ | 1 is an odd number | +-----------------------------------+ 1 row in set (0.02 sec) +-----------------------------------+ | CONCAT(sale, " is an odd number") | +-----------------------------------+ | 3 is an odd number | +-----------------------------------+ 1 row in set (0.05 sec) +-----------------------------------+ | CONCAT(sale, " is an odd number") | +-----------------------------------+ | 5 is an odd number | +-----------------------------------+ 1 row in set (0.09 sec) +-----------------------------------+ | CONCAT(sale, " is an odd number") | +-----------------------------------+ | 7 is an odd number | +-----------------------------------+ 1 row in set (0.12 sec) +-----------------------------------+ | CONCAT(sale, " is an odd number") | +-----------------------------------+ | 9 is an odd number | +-----------------------------------+ 1 row in set (0.17 sec) Query OK, 0 rows affected (0.00 sec)
REPEAT 和 UNTIL 语句可以用来创建一直重复直到遇到某些逻辑条件才终止的循环,REPEAT...UNTIL 循环的语法如下:
[label:] REPEAT statements UNTIL expression END REPEAT [label]
REPEAT 循环将会一直执行,直到定义在 UNTIL 子句中的测试条件 expression 为 TRUE 为止。基本上,REPEAT 循环逻辑上等同与 LOOP-LEAVE-END LOOP 块,例如:
some_label:LOOP statements IF expression THEN LEAVE some_label; END IF; END LOOP;
REPEAT 循环从某种程度上看起来更易维护,因为它清晰的给出了在什么条件下终止循环。LEAVE 在简单循环中可以出现在任何地方,而 UNTIL 语句总是伴随这 END REPEAT 子句出现在循环的最底端。此外,我们不必为 REPEAT 循环给出一个标签,因为 UNTIL 条件总是指向当前循环,当然我们还是建议你对 REPEAT 循环加上标签来改善可读性,特别是循环嵌套时。
示例:使用 REPEAT 来打印小于 10 的奇数
CREATE DEFINER=`root`@`localhost` PROCEDURE `demo`(IN sale int) BEGIN SET sale = 0; loop1: REPEAT SET sale = sale + 1; -- MOD 是内置函数,进行取模运算 IF MOD(sale, 2) <> 0 THEN -- 如果是奇数,则执行该语句 Select concat(sale, " is an odd number"); END IF; UNTIL sale >= 10 END REPEAT; END
调用存储过程:
mysql> call demo(30); +-----------------------------------+ | concat(sale, " is an odd number") | +-----------------------------------+ | 1 is an odd number | +-----------------------------------+ 1 row in set (0.04 sec) +-----------------------------------+ | concat(sale, " is an odd number") | +-----------------------------------+ | 3 is an odd number | +-----------------------------------+ 1 row in set (0.09 sec) +-----------------------------------+ | concat(sale, " is an odd number") | +-----------------------------------+ | 5 is an odd number | +-----------------------------------+ 1 row in set (0.15 sec) +-----------------------------------+ | concat(sale, " is an odd number") | +-----------------------------------+ | 7 is an odd number | +-----------------------------------+ 1 row in set (0.21 sec) +-----------------------------------+ | concat(sale, " is an odd number") | +-----------------------------------+ | 9 is an odd number | +-----------------------------------+ 1 row in set (0.27 sec) Query OK, 0 rows affected (0.00 sec)
注意:
(1)REPEAT 循环的循环体总是能确保至少运行一次,这意味这 UNTIL 将在第一次循环体被执行后才得到测试,对于那些不需要确保至少运行一次的而只有条件得到满足才运行的场合下,请使用 WHILE 循环。
(2)在 REPEAT 循环中使用 ITERATE 可能会导致不预期的结果,可能会在传递给 UNTIL 的条件尚未得到满足的情况下结束本次循环。
WHILE 循环只有在条件为真是才执行循环。如果条件不为真,那么循环体将永远的得不到执行。
WHILE 循环的语法如下:
[label:] WHILE expression DO statements END WHILE [label]
WHILE 循环的功能大致等同于简单的 LOOP-LEAVE-END LOOP 结构,它将 LEAVE 子句作为第一个语句。
示例:使用 WHILE 实现求得小于 10 的奇数
CREATE PROCEDURE demo(IN sale int) BEGIN SET sale = 1; loop1: WHILE sale <= 10 DO -- 如果是奇数,则输出信息 IF MOD(sale, 2) <> 0 THEN SELECT CONCAT(sale, " is an odd number"); END IF; SET sale = sale + 1; END WHILE loop1; END
调用存储过程:
mysql> call demo(30); +-----------------------------------+ | CONCAT(sale, " is an odd number") | +-----------------------------------+ | 1 is an odd number | +-----------------------------------+ 1 row in set (0.02 sec) +-----------------------------------+ | CONCAT(sale, " is an odd number") | +-----------------------------------+ | 3 is an odd number | +-----------------------------------+ 1 row in set (0.05 sec) +-----------------------------------+ | CONCAT(sale, " is an odd number") | +-----------------------------------+ | 5 is an odd number | +-----------------------------------+ 1 row in set (0.08 sec) +-----------------------------------+ | CONCAT(sale, " is an odd number") | +-----------------------------------+ | 7 is an odd number | +-----------------------------------+ 1 row in set (0.12 sec) +-----------------------------------+ | CONCAT(sale, " is an odd number") | +-----------------------------------+ | 9 is an odd number | +-----------------------------------+ 1 row in set (0.16 sec) Query OK, 0 rows affected (0.00 sec)
当然,我们还可以使用 LOOP-END LOOP 来实现 WHILE 循环的功能,例如:
CREATE PROCEDURE demo(IN sale int) BEGIN SET sale = 1; myloop: LOOP -- 退出循环 IF sale > 10 THEN LEAVE myloop; END IF; IF MOD(sale, 2) <> 0 THEN SELECT CONCAT(sale, " is an odd number"); END IF; SET sale = sale + 1; END LOOP myloop; END
嵌套循环是指在一个循环结构内部嵌套另一个循环结构的编程技术。嵌套循环允许在外层循环的每次迭代中,内层循环都会完整地执行一次。
嵌套循环通常用于处理多维数据结构,例如二维数组或矩阵。通过嵌套循环,可以遍历数组的每个元素,并对其进行相应的操作。
示例:我们使用嵌套 LOOP-LEAVE-END LOOP 结构打印了基本的 “时间表”
CREATE PROCEDURE demo(IN sale int) BEGIN DECLARE i, j INT DEFAULT 1; -- 外层循环 outer_loop: LOOP SET j = 1; -- 内存循环 inner_loop: LOOP SELECT concat(i, " times ", j, " is ", i*j); SET j = j + 1; IF j > 12 THEN -- 退出内存循环 LEAVE inner_loop; END IF; END LOOP inner_loop; SET i = i + 1; IF i > 12 THEN -- 退出外层循环 LEAVE outer_loop; END IF; END LOOP outer_loop; END
注意:当嵌套一个循环时,对于循环的开始和结束的进行标签命名可以使其看起来更清晰,同样对于我们分清循环的各个层次也有很大的帮助。当然,如果你需要使用 LEAVE,你就必须使用标签对循环命名。