条件控制语句允许你基于变量或表达式来执行代码。条件控制表达式允许你根据这些值或表达式返回值的不同而做出不同的行为,而这些值可以引用存储程序的参数、数据库中的数据、变量等等。
MySQL 存储程序语言支持两种不同的条件控制表达式:IF 和 CASE。
存储程序中 IF 语句的语法:
IF expression THEN commands [ELSEIF expression THEN commands ....] [ELSE commands] END IF;
上面语法中,expression 表示一个返回 TRUE、FALSE 或 NULL 的表达式,commands 表示一组待执行的表达式或者 SQL 语句。其中,[] 内部的 ELSEIF 和 ELSE 是可选的,即下面代码是正确的:
IF expression THEN commands END IF;
在 IF 或者 ELSEIF 语句中的命令只有在与之配合的表达式结果为 TRUE 时才会执行。例如:
IF age < 16 THEN 命令1 ELSEI age >= 16 THEN 命令2 END IF;
上面代码中,只有当 age < 16 为真时才会执行 “命令1” ,age >= 16 为真时才会执行 “命令2”。
假如你同时操作一个或多个变量,并且其中的一个变量值为 NULL,那么这个表达式的结果就是 NULL 而不是 TRUE 或 FALSE。
如果你认为代码中的测试条件不是 TRUE 就是 FALSE,就会产生一些错误的结果。例如:如果我们在版本字符串中不能找到 alpha 或 beta,我们就会以为这个发行版是正式的产品。如果 l_version 是 NULL,那么 ELSE 条件将总是被执行,这就存在逻辑错误。脚本如下:
IF (INSTR(l_version_string,'alpha')>0) THEN SELECT 'Alpha release of MySQL'; ELSEIF (INSTR(l_version_string,'beta')>0) THEN SELECT 'Beta release of MySQL'; ELSE SELECT 'Production release of MySQL'; END IF;
注意:
(1)不要假设测试表达式的结果不是 TRUE 就是 FALSE。如果表达式中的任何一个变量为 NULL,也可以返回 NULL(UNKNONN)。
(2)任何返回数字值或字符串的表达式看上去是数字,但测试结果可能是 TRUE,FALSE 或 NULL。规则如下:
a、任何绝对值大于 1 的数字表达式,在 IF 或 ELSEIF 语句的测试结果为 TRUE。绝对值意味这无论是 1 还是 -1 测试结果都为 TRUE。
b、如果数字值表达式为 0,则测试结果为 FALSE
在最简单的形式中,IF 可以被指向一个语句集合,只要其中的一个条件测试为 TRUE 就执行,这种 IF 表达式的语法如下:
IF expression THEN statements END IF;
其中,expression 是一个表达式,statements 是 IF 语句中要执行的逻辑语句。
三值逻辑
布尔表达式可以返回三个可能的结果,当布尔表达式中的值都是已知时,结果可能是 TRUE 或 FALSE。例如,下面的表达式会返回 TRUE 或 FALSE:
(2 < 3) AND (5 < 10)有时,你可能并不知道所有表达式的值,这是因为数据库允许值为 NULL。那么随后会发生什么,从表达式返回的结果可以包含 NULL 吗?请看例子:
2 < NULL因为你不知道遗漏的值是哪一个,你所能给的唯一答案就是 “UNKNONN” ,所谓的 “三值逻辑” 指你所能得到的可能答案不仅是 TRUE 和 FALSE,还有 NULL。
(1)展示了简单的 IF 语句用法,如果输入的值大于 20 则输出一个字符串,如下:
CREATE PROCEDURE demo(IN sale int) BEGIN -- if 语句简单使用 IF sale > 20 THEN select 'sale > 20'; END IF; END
调用存储过程:
mysql> call demo(30); +-----------+ | sale > 20 | +-----------+ | sale > 20 | +-----------+ 1 row in set (0.03 sec) Query OK, 0 rows affected (0.00 sec)
(2)在 THEN 和 END IF 子句中包含多个语句,例如:
CREATE DEFINER=`root`@`localhost` PROCEDURE `demo`(IN sale int) BEGIN -- if 语句简单使用 IF sale > 20 THEN set sale = sale + 10; select concat('sale=', sale) as 'new sale'; END IF; END
调用存储过程:
mysql> call demo(30); +----------+ | new sale | +----------+ | sale=40 | +----------+ 1 row in set (0.03 sec) Query OK, 0 rows affected (0.00 sec)
(3)在 IF 语句中包含任何其他可执行语句,如循环语句、SET 语句和别的 IF 语句(即嵌套 IF 语句)。例如:
CREATE PROCEDURE demo(IN sale int) BEGIN -- if 语句简单使用 IF sale IS NOT NULL THEN IF sale > 20 THEN select concat('sale=', sale); END IF; END IF; END
调用存储过程:
mysql> call demo(30); +-----------------------+ | concat('sale=', sale) | +-----------------------+ | sale=30 | +-----------------------+ 1 row in set (0.03 sec) Query OK, 0 rows affected (0.00 sec)
(4)没有必要手动为 IF 语句断行,在下面例子中的所有 IF 语句都被 MySQL 一视同仁,例如:
-- 格式一 IF sale_value > 200 THEN select concat('sale=', sale); END IF; -- 格式二 IF sale_value > 200 THEN select concat('sale=', sale); END IF; -- 格式三(推荐此种格式) IF sale_value > 200 THEN select concat('sale=', sale); END IF;
注意了,虽然 MySQL 对上诉三种写法均能执行,如果 IF 语句非常简单,则放在同一行上是没有问题的,但是对于复杂的或者嵌套的 IF 结构而言这却不是一种很
好的编程实践。例如,以下这种格式更易读,易理解,易维护:
IF sale_value > 200 THEN CALL apply_free_shipping(sale_id); IF sale_value > 500 THEN CALL apply_discount(sale_id,20); END IF; END IF;
或者是:
IF sale_value > 200 THEN CALL apply_free_shipping(sale_id); IF sale_value > 500 THEN CALL apply_discount(sale_id,20);END IF;END IF;
某些程序员喜欢将 THEN 子句放在独立的一行上,如下所示:
IF sale_value > 200 THEN CALL apply_free_shipping(sale_id); END IF;
但这更像是个人的喜好或者编程标准,对于一个复杂的 IF 语句而言,使用缩排和格式化来确保你的 IF 语句的逻辑更容易理解是非常有必要的。
为你的 IF 语句增加 ELSE 条件允许你在假设 IF 条件非真时执行你所需的代码,我们还是要再次强调一下非真并不总代表假。如果 IF 语句条件测试为假,那么 ELSE 语句任然会被执行;这当你无法保证 IF 条件中出现 NULL 值时可能会带来微妙的错误。
IF-THEN-ELSE 块有如下语法:
IF expression THEN -- 当 expression 为 TRUE 时执行的语句 ELSE -- 当 expression 为 FALSE 或 NULL 时执行的语句 END IF;
示例:如果参数 sale 大于 20 则输出 “sale 大于 20” 字符串,否则输出 “sale 不大于 20” 字符串,例如:
CREATE PROCEDURE demo(IN sale int) BEGIN IF sale > 20 THEN select 'sale 大于 20' as message; ELSE select 'sale 不大于 20' as message; END IF; END
调用存储过程:
mysql> call demo(30); +--------------+ | message | +--------------+ | sale 大于 20 | +--------------+ 1 row in set (0.02 sec) Query OK, 0 rows affected (0.00 sec)
IF 语句的完整语法允许你定义多个条件。第一个测试为 TRUE 的条件将被执行。如果没有任何表达式测试为 TRUE,那么 ELSE 子句(如果存在)将被执行。
IF-THEN-ELSEIF-ELSE 语句的语法:
IF expression1 THEN -- 当表达式 expression1 为 TRUE 时执行 ELSEIF expression2 THEN -- 当表达式 expression1 为 FALSE,expression2 为 TRUE 是执行 ELSE -- 当表达式 expression1 和 expression2 均为 FALSE,或者为 NULL 时执行 END IF;
注意,你可以在其中放置任意多的 ELSEIF 条件,条件之间并非互相排斥。这意味着,不止一个条件可能被测试为真。而第一个被测试为真的条件将会得到执行。创
建叠加的条件看起来会很有用,但你排放条件时必须非常小心。例如:
IF (sale_value > 200) THEN -- 语句1 ELSEIF (sale_value > 200 and customer_status='PREFERRED') THEN -- 语句2 END IF;
上面代码的意图非常明确:如果 sale_value 大于 200,则执行语句1,如果 sale_value 大于 200,且 customer_status 等于 “PREFERRED”,则执行语句2。但是,语句2永远得不到执行。
那么,怎样改进上面的语句,使它变得严谨,我们可以将 ELSEIF 条件分支移入 IF 子句中来确保它首先得到测试,例如:
IF (sale_value > 200 and customer_status='PREFERRED') THEN -- 语句2 ELSEIF (sale_value > 200) THEN -- 语句1 END IF;
或者,我们可以将一个包含 sale_value>200 条件的 IF 语句嵌套一个 IF 子句中来测试客户的状态,例如:
IF (sale_value > 200) THEN -- 语句1 IF (customer_satus='PREFERRED') THEN -- 语句2 END IF; END IF;
一般来说我们总是希望尽可能避免使用嵌套 IF 语句,但是当存在大量的 sale_value 大于 200 的条件分支需要我们去处理,那么对 sale_value 进行一次测试,然后对其他个别的条件进行测试就变得有意义。例如:
IF (sale_value > 200 and customer_status='PLATINUM') THEN -- 语句1 -- 语句2 ELSEIF (sale_value > 200 and customer_status='GOLD') THEN -- 语句1 -- 语句3 ELSEIF (sale_value > 200 and customer_status='SILVER') THEN -- 语句1 -- 语句4 ELSEIF (sale_value > 200 and customer_status='BRONZE') THEN -- 语句1 -- 语句5 ELSEIF (sale_value > 200) THEN -- 语句1 END IF;
上面例子中,不断的重复着的 “sale_value > 200” 条件和 “语句1”,实际上渐渐的破坏了代码的可读性,并且使得效率降低。如果使用嵌套的 IF 语句来清晰的给每个 “sale_value > 200” 条件执行 “语句1”。然后,根据不同的 “customer_status” 值来执行不同的语句,从而来避免过长的测试条件,例如:
IF (sale_value > 200) THEN -- 语句1 IF (customer_status='PLATINUM') THEN -- 语句2 ELSEIF (customer_status='GOLD') THEN -- 语句3 ELSEIF (customer_status='SILVER') THEN -- 语句4 ELSEIF (customer_status='BRONZE') THEN -- 语句5 END IF; END IF;