|
3#
楼主 |
发表于 2008-1-17 11:31:30
|
只看该作者
Example 4-6 展示了简单的IF语句
Example 4-6. IF语句的简单例子
- IF sale_value > 200 THEN
-
- CALL apply_free_shipping(sale_id);
- END IF
- ;
复制代码
我们可以THEN和END IF子句中包含多个语句,就像Example 4-7
Example 4-7. IF语句中的多个语句
-
- IF sale_value > 200 THEN
- CALL apply_free_shipping(sale_id);
- CALL apply_discount(sale_id,10);
- END IF;
复制代码
正如Example 4-8所展示的那样,我们同样可以在IF语句中包含任何可执行语句,比如循环结果,SET语句和别的IF语句(当然,正如你即将看到的一样,我们竟可能在这种形式中避免使用嵌套IF语句)。
Example 4-8.嵌套IF语句
-
- IF sale_value > 200 THEN
- CALL apply_free_shipping(sale_id);
- CALL apply_discount(sale_id,10);
- END IF;
复制代码
正如Example 4-9所示,没有必要为IF语句断行,在这个例子中的所有IF语句都被MySQL一视同仁。
Example 4-9.IF语句的候选格式
-
- IF sale_value > 200 THEN CALL apply_free_shipping(sale_id); END IF;
-
-
- IF sale_value > 200
- THEN
- CALL apply_free_shipping(sale_id);
- END IF;
-
-
- IF sale_value > 200 THEN
- CALL apply_free_shipping(sale_id);
- END IF;
复制代码
大体上把非常简单的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语句的逻辑更容易理解
|
4.2.1.3. IF-THEN-ELSE语句
为你的IF语句增加ELSE条件允许你在假设IF条件非真时执行你所需的代码,我们还是要再次强调一下非真并不总代表假。如果IF语句条件测试为假,那么ELSE语句任然会被执行;这当你无法保证IF条件中出现NULL值时可能会带来微妙的错误。
IF-THEN-ELSE块有如下语法:
IF expression THEN
statements that execute if the expression is TRUE
ELSE
statements that execute if the expression is FALSE or NULL
END IF;
所以我们在Example 4-10中,我们在销售额低于$200时对某个订单进行托运,否则我们就对其进行折扣处理(并且不处理托运)
IF sale_value <200 THEN
CALL apply_shipping(sale_id);
ELSE
CALL apply_discount(sale_id);
END IF;
4.2.1.4. IF-THEN-ELSEIF-ELSE语句
IF语句的完整语法允许你定义多个条件。第一个测试为TRUE的条件将被执行。如果没有任何表达式测试为TRUE,那么ELSE子句(如果存在)将被执行。IF-THEN-ELSEIF-ELSE语句的语法是这样的
IF expression THEN
statements that execute if the expression is TRUE
ELSEIF expression THEN
statements that execute if expression1 is TRUE
ELSE
statements that execute if all the preceding expressions are FALSE or NULL
END IF;
你可以在其中放置任意多的ELSEIF条件
条件之间并非互相排斥。这意味着,不止一个条件可能被测试为真。而第一个被测试为真的条件将会得到执行。创建叠加的条件看起来会很有用,但你排放条件时必须非常小心。举例来说,考虑一下Example 4-11的IF-ELSEIF语句。
Example 4-11.使用叠加条件的IF-ELSEIF块的例子
-
- IF (sale_value>200) THEN
- CALL free_shipping(sale_id);
- ELSEIF (sale_value >200 and customer_status='PREFERRED') THEN
- CALL free_shipping(sale_id);
- CALL apply_discount(sale_id,20);
- END IF;
复制代码
这段代码的意图非常明确:对于订购超过$200的客户提供免费托运,并且给与优先客户以20%的折扣,虽然因为所有超过$200的订购将使第一个条件测试为真,但是对于ELSEIF中订购超过$200的测试将永远得不到执行,这意味着优先的客户将不能得到他们的折扣,客户没有得到折扣意味着我们的存储过程程序员就得不到年终的奖金!
有一大堆的方法可以让这个语句变得更严谨:其中的一种是,我们可以将ELSEIF条件分支移入IF子句中来确保它首先得到测试,或者,我们可以将一个包含sale_value>200条件的IF语句嵌套与IF子句中来测试客户的状态,就像Example 4-12所展示的那样
Example 4-12. 两种纠正前面例子中逻辑错误的方面
-
- /* Reordering the IF conditions */
- IF (sale_value >200 and customer_status='PREFERED') THEN
- CALL free_shipping(sale_id);
- CALL apply_discount(sale_id,20);
- ELSEIF (sale_value>200) THEN
- CALL free_shipping(sale_id);
-
- END IF;
-
-
- /* Nesting the IF conditions */
-
-
- IF (sale_value >200) THEN
- CALL free_shipping(sale_id);
- IF (customer_satus='PREFERRED') THEN
- CALL apply_discount(sale_id,20);
- END IF;
- END IF:
复制代码
在Example 4-12中的两种替换方案几乎都是完美有效的。一般来说我们总是希望尽可能避免嵌套IF语句,但是当存在大量的sale_value大于200的条件分支需要我们去处理,那么对sale_value进行一次测试,然后对其他个别的条件进行测试就变得有意义。所以我们的业务规则就是给订购超过$200的客户以免费托运,并且让我们的客户忠实度程序基于客户的状态来给出不同的折扣率。这种在IF-ELSEIF块中的单个逻辑就像Example 4-13所演示的一样。
Example 4-13.IF块伴随着大量的冗长的条件
-
- IF (sale_value >200 and customer_status='PLATINUM') THEN
- CALL free_shipping(sale_id); /* Free shipping*/
- CALL apply_discount(sale_id,20); /* 20% discount */
-
-
- ELSEIF (sale_value >200 and customer_status='GOLD') THEN
- CALL free_shipping(sale_id); /* Free shipping*/
- CALL apply_discount(sale_id,15); /* 15% discount */
-
- ELSEIF (sale_value >200 and customer_status='SILVER') THEN
- CALL free_shipping(sale_id); /* Free shipping*/
- CALL apply_discount(sale_id,10); /* 10% discount */
-
-
- ELSEIF (sale_value >200 and customer_status='BRONZE') THEN
- CALL free_shipping(sale_id); /* Free shipping*/
- CALL apply_discount(sale_id,5); /* 5% discount*/
-
- ELSEIF (sale_value>200) THEN
- CALL free_shipping(sale_id); /* Free shipping*/
-
- END IF;
复制代码
在这个例子中不变的重复着的sale_value条件和free_shipping调用,实际上渐渐的破坏了代码的可读性,并且是效率降低(见第22章)。如果使用嵌套的IF结构来清晰的给每个超过$200的客户以免费托运,而后,根据仅客户忠诚度给与相应的折扣会显得更好。Example 4-14展示了嵌套IF的实现版本。
Example 4-14.使用嵌套IF来避免呈长的测试条件
-
- IF (sale_value > 200) THEN
- CALL free_shipping(sale_id); /*Free shipping*/
-
-
- IF (customer_status='PLATINUM') THEN
- CALL apply_discount(sale_id,20); /* 20% discount */
-
-
- ELSEIF (customer_status='GOLD') THEN
- CALL apply_discount(sale_id,15); /* 15% discount */
-
-
- ELSEIF (customer_status='SILVER') THEN
- CALL apply_discount(sale_id,10); /* 10% discount */
-
-
- ELSEIF (customer_status='BRONZE') THEN
- CALL apply_discount(sale_id,5); /* 5% discount*/
- END IF;
-
-
- END IF;
复制代码
4.2.2. CASE语句
CASE语句是一种条件执行或者流程控制的可替换形式。任何CASE语句能做的事都可以用IF语句完成(或者正好相反),但是CASE语句通常更可读并且在处理多个测试条件时更有效,特别是当所有的条件都输出比对同一个表达式时
4.2.2.1. 简单CASE语句
CASE语句有两种形式。第一种形式通常被称为简单CASE语句,用来作为多个表达式的输出相比对
CASE expression
WHEN value THEN
statements
[WHEN value THEN
statements ...]
[ELSE
statements]
END CASE;
这种语法在检测若干清晰的表达式输出集合时会很有用。举例来说,我们可以使用简单CASE语句来检查前一个样例中的客户忠诚度状态,就像Example 4-15所示
Example 4-15.简单CASE表达式的例子
-
- CASE customer_status
- WHEN 'PLATINUM' THEN
- CALL apply_discount(sale_id,20); /* 20% discount */
-
- WHEN 'GOLD' THEN
- CALL apply_discount(sale_id,15); /* 15% discount */
-
- WHEN 'SILVER' THEN
- CALL apply_discount(sale_id,10); /* 10% discount */
-
-
- WHEN 'BRONZE' THEN
- CALL apply_discount(sale_id,5); /* 5% discount*/
- END CASE;
复制代码
就像IF命令一样,你可以指定多个WHEN语句并且你可以指定一个ELSE子句来执行其他条件未得到满足时的情况
当然,你必须清醒的认识到CASE语句将会在没有任何条件匹配的情况下产生异常,这意味着在Example 4-15中如果customer_status不是'PLATINUM', 'GOLD', 'SILVER'或 'BRONZE' 其中的任何一个,那么接下来就会发生运行时异常:
ERROR 1339 (20000): Case not found for CASE statement
我们可以为此创建一个异常处理单元来忽略这种错误(就像第六章中所描述的一样),但是也许使用一个ELSE子句来确保所有可能的条件都得到处理是一种更好的编程实践。因此,我们或许应该适应前面的例子,给其增加一个ELSE子句来给那些没有遇到过前面这种情况的客户以0折扣率
提示:如果没有任何一个CASE语句与输入的条件相匹配,CASE将会生成一个MySQL 1339错误。你可以为其创建一个错误处理单元来忽略错误,或者在CASE语句中使用ELSE子句确保异常永远不再发生
|
在简单CASE语句中比对一系列表达式的值显得很有用。虽然,简单的CASE语句并不能轻易的,完全的匹配范围,或者处理更为复杂的包含 多表达式的条件,因此,对于那些更为复杂的“情况”我们可以使用“查询”CASE语句,将在下一节中被描述。
4.2.2.2. "查询" CASE语句
查询CASE语句和IF-ELSEIF-ELSE-END块所具备的功能基本等同,查询CASE语句具有如下语法:
CASE
WHEN condition THEN
statements
[WHEN condition THEN
statements...]
[ELSE
statements]
END CASE;
使用查询CASE结构,我们可以实现早些段落中是用IF来实现的免费托运和折扣率的逻辑。在Example 4-16中我们展示了是用查询CASE语句来是先的销售折扣率和免费托运的逻辑。
Example 4-16. 查询CASE表达式的例子
-
- CASE
- WHEN (sale_value >200 AND customer_status='PLATINUM') THEN
- CALL free_shipping(sale_id); /* Free shipping*/
- CALL apply_discount(sale_id,20); /* 20% discount */
-
-
- WHEN (sale_value >200 AND customer_status='GOLD') THEN
- CALL free_shipping(sale_id); /* Free shipping*/
- CALL apply_discount(sale_id,15); /* 15% discount */
-
-
- WHEN (sale_value >200 AND customer_status='SILVER') THEN
- CALL free_shipping(sale_id); /* Free shipping*/
- CALL apply_discount(sale_id,10); /* 10% discount */
-
-
- WHEN (sale_value >200 AND customer_status='BRONZE') THEN
- CALL free_shipping(sale_id); /* Free shipping*/
- CALL apply_discount(sale_id,5); /* 5% discount*/
-
- WHEN (sale_value>200) THEN
- CALL free_shipping(sale_id); /* Free shipping*/
-
-
-
- END CASE;
复制代码
虽然请始终记住如果没有任何WHERE子句被匹配时,将会产生一个1339错误,这些代码将在订单少于$200或者客户并不在我们的忠诚度程序时会导致致命的错误,因此我们必须插入ELSE子句来保护我们的代码和工作的安全,正如Example 4-17所做的那样。
Example 4-17为我们的查询CASE例子增加一个“哑”ELSE子句
-
- CASE
- WHEN (sale_value >200 AND customer_status='PLATINUM') THEN
- CALL free_shipping(sale_id); /* Free shipping*/
- CALL apply_discount(sale_id,20); /* 20% discount */
-
-
- WHEN (sale_value >200 AND customer_status='GOLD') THEN
- CALL free_shipping(sale_id); /* Free shipping*/
- CALL apply_discount(sale_id,15); /* 15% discount */
-
-
- WHEN (sale_value >200 AND customer_status='SILVER') THEN
- CALL free_shipping(sale_id); /* Free shipping*/
- CALL apply_discount(sale_id,10); /* 10% discount */
-
-
- WHEN (sale_value >200 AND customer_status='BRONZE') THEN
- CALL free_shipping(sale_id); /* Free shipping*/
- CALL apply_discount(sale_id,5); /* 5% discount*/
-
- WHEN (sale_value>200) THEN
- CALL free_shipping(sale_id); /* Free shipping*/
-
-
-
- END CASE;
复制代码
注意因为MySQL在存储程序语言中缺乏NULL语句(不干任何事的),所以我们必须加入一个哑语句,但是这个语句开销几乎为0
就像我们用IF所实现的逻辑一样,我们可以使用嵌套CASE语句来是先出一个逻辑更为清晰的版本。在Example 4-18中我们结合了简单CASE和查询CASE语句,并且包含了一个“not found”的错误处理单元来避免使用ELSE语句。我们使用块来闭合其中的内容,因此我们的处理单元并不会感应到此存储程序中的其他语句
Example 4-18.嵌套CASE语句和一个块作用域的“not found”处理单元
-
- BEGIN
- DECLARE not_found INT DEFAULT 0;
- DECLARE CONTINUE HANDLER FOR 1339 SET not_found=1;
-
-
- CASE
- WHEN (sale_value>200) THEN
- CALL free_shipping(sale_id);
- CASE customer_status
- WHEN 'PLATINUM' THEN
- CALL apply_discount(sale_id,20);
- WHEN 'GOLD' THEN
- CALL apply_discount(sale_id,15);
- WHEN 'SILVER' THEN
- CALL apply_discount(sale_id,10);
- WHEN 'BRONZE' THEN
- CALL apply_discount(sale_id,5);
- END CASE;
- END CASE;
-
-
- END;
复制代码
4.2.3 IF和CASE的比较
我们已经看到无论是IF还是CASE语句都能实现相同的流程控制功能。那么哪一个是最好的?在很大程度上,对两者的选择更关乎与个人风格和编程标准,而并非取决于两者所暗示的优势。尽管如此,我们在对CASE和IF进行选择时,可以遵照如下的原则:
一致的风格往往比两者在其各自的特殊场合所显现的微弱优势更重要。我们更希望你能够一致的选择你的CASE或者IF,而不是根据你的心情,天气或者掷色子来随机的决定使用哪一个
当你使用一个表达式来和一列清晰的值相比对时CASE会略显清晰(使用“简单”CASE语句)。
当你基于多个变量对一列复杂表达式进行测试时,IF结构可能会显得更为有好并容易理解
如果你选择CASE,你必须保证至少一个CASE条件得到匹配,或者定义一个错误处理单元来捕获错误,如果没有任何CASE条件得到满足,那么使用IF就没有这种烦恼。
无论你选择哪一个结构,请记住一下要点
如果CASE或IF结构中的条件得到满足,则别的条件将不再得到测试,这意味这你的条件排放顺序需要相当的严格
MySQL存储程序语言使用三值逻辑;因此若是一个语句是非真并不意味着他必定为FALSE,也可能是NULL。
你必须仔细的思考你代码的可读性,优势使用IF或CASE嵌套集可以使代码更可读,更有效。虽然在通常状况下应该避免嵌套的发生,特别是那些嵌套层次很深的代码(这里指的是三层或三层以上)。 |
|