本文内容均翻译自官方文档
========================================================
1. IF-THEN
是使用IF最简单的形式,如果条件为真,则执行,否则跳过。
Example:
IF v_user_id <> 0 THEN UPDATE users SET email = v_email WHERE user_id = v_user_id; END IF; |
2. IF-THEN-ELSE
它允许指定一组备选语句,如果条件不为真,则应该执行这些语句。(注意,这包括条件为空的情况)。
Example:
IF parentid IS NULL OR parentid = '' THEN RETURN fullname; ELSE RETURN hp_true_filename(parentid) || '/' || fullname; END IF; |
3. IF-THEN-ELSIF
有时有两个以上的选择。IF-T-ELSIF提供了一种方便的检查几种替代方案的方法。对IF条件进行连续测试直到找到第一个IF条件为止。然后执行关联的语句,之后控制在结束if之后传递给下一个语句。(任何后续的if条件未被测试)如果没有IF条件是真的,则执行另一个块(如果有的话)。
IF boolean-expression THEN statements [ ELSIF boolean-expression THEN statements [ ELSIF boolean-expression THEN statements ...]] [ ELSE statements ] END IF; |
4. CASE
简单的CASE形式提供了基于操作数相等的条件执行。搜索表达式被计算(一次),并依次与WHEN子句中的每个表达式进行比较。如果找到匹配,则执行相应的语句,然后控制传递到END CASE之后的下一个语句。如果没有找到匹配,则执行ELSE语句;但是如果不存在ELSE,则引发CASE_NOT_FOUND异常。
CASE search-expression WHEN expression [, expression [ ... ]] THEN statements [ WHEN expression [, expression [ ... ]] THEN statements ... ] [ ELSE statements ] END CASE; |
5. Searched CASE
CASE的搜索形式基于布尔表达式的真实性提供有条件的执行。每个子句的布尔表达式依次进行评估,直到找到一个正确的表达式。然后执行相应的语句,然后控制在结束情况后传递给下一个语句。(当表达式未被计算时)。如果没有找到真正的结果,则执行ELSE语句;但是如果不存在其他语句,则引发一个CaseSoNoSyDebug异常。
CASE WHEN boolean-expression THEN statements [ WHEN boolean-expression THEN statements ... ] [ ELSE statements ] END CASE; |
1. LOOP
循环定义一个无条件循环,该循环无限期地重复,直到退出或返回语句终止为止。嵌套循环中的EXIT和CONTINUE语句可以使用可选的标签来指定这些语句引用哪个循环。
[ <<label>> ] LOOP statements END LOOP [ label ]; |
2. EXIT
如果没有给出标签,则最内层循环被终止,接下来执行结束语句循环语句。如果给出标签,它必须是嵌套循环或块的当前或某个外部级别的标签。然后,终止循环或块,并在循环/块的相应端之后继续进行语句控制。
如果指定了,则只有在布尔表达式为真时才出现循环退出。否则,控件传递到退出后的语句。
退出可用于所有类型的循环;它不限于使用无条件循环。
当与开始块一起使用时,Exchange将控件传递给块结束后的下一个语句。注意,必须为此使用标签;未标记的退出永远不会被认为与开始块匹配。(这是PostgreSQL的前8个版本的更改,这将允许未标记的退出与开始块匹配)。
EXIT [ label ] [ WHEN boolean-expression ]; |
Examples:
LOOP -- some computations IF count > 0 THEN EXIT; -- exit loop END IF; END LOOP; LOOP -- some computations EXIT WHEN count > 0; -- same result as previous example END LOOP; <<ablock>> BEGIN -- some computations IF stocks > 100000 THEN EXIT ablock; -- causes exit from the BEGIN block END IF; -- computations here will be skipped when stocks > 100000 END; |
3. CONTINUE
如果没有给出标签,最内层循环的下一次迭代就开始了。也就是说,跳过循环主体中剩余的所有语句,并且控制返回到循环控制表达式(如果有的话)以确定是否需要另一个循环迭代。如果存在标签,则它指定将继续执行的循环的标签。
如果指定了,则只在布尔表达式为真时才开始循环的下一次迭代。否则,控件在继续后传递给语句。
继续可用于所有类型的循环;它不限于使用无条件循环。
CONTINUE [ label ] [ WHEN boolean-expression ]; |
Examples:
LOOP -- some computations EXIT WHEN count > 100; CONTINUE WHEN count < 50; -- some computations for count IN [50 .. 100] END LOOP; |
4. WHILE
只要布尔表达式求值为true,while语句就重复一系列语句。在循环体的每个条目之前检查表达式。
[ <<label>> ] WHILE boolean-expression LOOP statements END LOOP [ label ]; |
Example:
WHILE amount_owed > 0 AND gift_certificate_balance > 0 LOOP -- some computations here END LOOP; WHILE NOT done LOOP -- some computations here END LOOP; |
5. FOR (Integer Variant)
这种形式的FOR创建一个遍历整数值范围的循环。变量名自动定义为类型整数,并且只存在于循环中(在循环中忽略变量名的任何现有定义)。两个表达式给出下限和上界的范围时,一旦进入循环进行评估。如果BY子句没有指定,迭代步骤是1,否则它是BY子句中指定的值,在循环条目中再次计算一次。如果指定反向,则在每次迭代之后减去步骤值,而不是添加。
[ <<label>> ] FOR name IN [ REVERSE ] expression .. expression [ BY expression ] LOOP statements END LOOP [ label ]; |
Examples
FOR i IN 1..10 LOOP -- i will take on the values 1,2,3,4,5,6,7,8,9,10 within the loop END LOOP; FOR i IN REVERSE 10..1 LOOP -- i will take on the values 10,9,8,7,6,5,4,3,2,1 within the loop END LOOP; FOR i IN REVERSE 10..1 BY 2 LOOP -- i will take on the values 10,8,6,4,2 within the loop END LOOP; |
1. Looping Through Query Results
如果循环由EXIT语句终止,则最后一个分配的行值仍然可以在循环之后访问。
此类FOR语句中使用的查询可以是向调用者返回行的任何SQL命令:SELECT是最常见的情况,但是您也可以使用INSERT、UPDATE或DELETE以及RETURNING子句。一些实用命令,如解释也会起作用。
[ <<label>> ] FOR target IN query LOOP statements END LOOP [ label ]; [ <<label>> ] FOR target IN EXECUTE text_expression [ USING expression [, ... ] ] LOOP statements END LOOP [ label ]; |
这与前面的表单类似,只是查询结果被指定为字符串表达式,在FOR循环的每个条目上计算并重新命名该表达式。这允许程序员选择预先计划的查询的速度或动态查询的灵活性,就像使用普通的EXECUTE语句一样。与执行一样,参数值可以通过使用方式插入到动态命令中。
2. Looping Through Arrays
FOREACH循环非常类似于FOR循环,但是它不是遍历SQL查询返回的行,而是遍历数组值的元素。(通常,FOREACH用于循环复合值表达式的组件;将来可以添加除数组之外的用于循环复合的变体。)
[ <<label>> ] FOREACH target [ SLICE number ] IN ARRAY expression LOOP statements END LOOP [ label ]; |
如果没有SLICE,或者如果指定了SLICE 0,循环迭代通过计算表达式生成的数组的各个元素。目标变量按顺序分配每个元素值,并且为每个元素执行循环体。
不管数组维数的多少,都以存储顺序访问元素。虽然目标通常只是单个变量,但是当循环遍历复合值(记录)数组时,它可以是变量列表。在这种情况下,对于每个数组元素,变量是从复合值的连续列分配的。
使用正向切片值,FOREACH迭代数组的切片而不是单个元素。切片值必须是不大于数组维数的整数常量。目标变量必须是数组,并且它接收数组值的连续切片,其中每个切片具有SLICE指定的维度数。这里是一个迭代通过一维切片的例子:
3. Trapping Errors
默认情况下,在PL/pgSQL函数中发生的任何错误都会中止该函数的执行,实际上也会中止周围事务的执行。通过使用带有异常子句的开始块,可以捕获错误并从中恢复错误。
[ <<label>> ] [ DECLARE declarations ] BEGIN statements EXCEPTION WHEN condition [ OR condition ... ] THEN handler_statements [ WHEN condition [ OR condition ... ] THEN handler_statements ... ] END; |
如果没有发生错误,则这种形式的块仅执行所有语句,然后控制传递到END之后的下一个语句。但是,如果在语句中发生错误,则放弃对语句的进一步处理,并将控制传递给EXCEPTION列表。在列表中搜索与发生的错误匹配的第一条件。如果找到匹配项,则执行相应的handler_statements,然后控制在结束后传递到下一个语句。如果没有找到匹配,错误就传播出去,好像EXCEPTION子句根本不存在:错误可以由带有EXCEPTION的封闭块捕获,或者如果没有,则中断函数的处理。
条件名称可以是附录A中显示的任何一个。类别名称匹配其类别内的任何错误。除了QUERY_CANCELED和ASSERT_FAILURE之外,其他条件都匹配其他错误类型。(可以通过名称捕获这两种错误类型是可能的,但往往不明智。)条件名称不是区分大小写的。此外,错误条件可以由SQLSTATE代码指定。
如果在所选的handler_statements中发生新错误,则此EXCEPTION子句无法捕获该错误,但会传播出去。周围的例外条款可以抓住它。
当EXCEPTION子句捕获错误时,PL/pgSQL函数的本地变量与发生错误时保持原样,但是回滚块中对持久数据库状态的所有更改。
异常处理程序经常需要识别发生的特定错误。有两种方法可以获得关于PL/pgSQL中当前异常的信息:特殊变量和GET STACKED DIAGNOSTICS命令。
在异常处理程序中,特殊变量SQLSTATE包含与引发的异常对应的错误代码(可能的错误代码列表参见表A.1)。特殊变量SqLSRM包含与异常相关联的错误消息。这些变量在异常处理程序之外是未定义的。
在异常处理程序中,还可以通过使用GET STACKED DIAGNOSTICS命令检索关于当前异常的信息。
每个项是一个关键字,标识要分配给指定变量(它应该是接收该变量的正确数据类型)的状态值。
Error Diagnostics Items
Name |
Type |
Description |
RETURNED_SQLSTATE |
text |
the SQLSTATE error code of the exception |
COLUMN_NAME |
text |
the name of the column related to exception |
CONSTRAINT_NAME |
text |
the name of the constraint related to exception |
PG_DATATYPE_NAME |
text |
the name of the data type related to exception |
MESSAGE_TEXT |
text |
the text of the exception's primary message |
TABLE_NAME |
text |
the name of the table related to exception |
SCHEMA_NAME |
text |
the name of the schema related to exception |
PG_EXCEPTION_DETAIL |
text |
the text of the exception's detail message, if any |
PG_EXCEPTION_HINT |
text |
the text of the exception's hint message, if any |
PG_EXCEPTION_CONTEXT |
text |
line(s) of text describing the call stack at the time of the exception |
4. Obtaining Execution Location Information
GET DIAGNOSTICS命令检索关于当前执行状态的信息。它的PG_CONTEXT状态项对于识别当前执行位置是有用的。PG_CONTEXT返回一个文本字符串,其中包含描述调用堆栈的文本行。第一行是指当前函数,当前正在执行GET DIAGNOSTICS 命令。第二个和任何后续行引用调用栈上的调用函数。
GET STACKED DIAGNOSTICS ... PG_EXCEPTION_CONTEXT 返回相同类型的堆栈跟踪,但是描述检测到错误的位置,而不是当前位置。