控制结构
📄字数 6.3K
👁️阅读量 加载中...
PL/SQL 的控制结构用于控制程序的执行流程,主要分为三大类:条件语句、循环语句和跳转语句。这些结构允许你根据条件执行不同的代码块,重复执行特定操作,或改变程序的执行顺序。
一、条件语句
在 PL/SQL 中,条件语句用于根据不同的条件执行不同的代码块。主要的条件控制结构包括 IF-THEN-ELSE 和 CASE 语句,它们提供了灵活的逻辑判断能力。
1.1 IF 语法格式
1.2 IF 参数说明
bool_expr
:布尔表达式,如果该表达式的值为真,则执行 THEN 后面的 pl_stmt_list;若为假且有 ELSIF 则依次进行条件判断,若均未匹配成功则执行 ELSE 分支(若有)pl_stmt_list 或退出该条件构造逻辑。ELSIF
:ELSIF 关键字用于添加额外的条件判断,可以有多个ELSIF子句。ELSE
:ELSE 关键字用于指定当所有 IF 和 ELSIF 条件都不满足时执行的语句。name_space
:结束 IF 语句后可带名空间(编译器不检查),用于标识具体的 IF 语句,通常用于嵌套的 IF 语句中。
1.3 CASE 语法格式
1.4 CASE 参数说明
case_arg
:可选参数。case_arg
表达式为When_item
中的条件清单,该参数类型同When_item
中条件清单类型一致,通过与When_item
中条件清单按顺序依次比较,若相同则为 true 并执行 THEN 后面的 pl_stmt_list,剩下的 CASE 部分将会被自动忽略。WHEN bool_expr THEN pl_stmt_list
:用于选择判断,类似于IF..ELSE
。若case_arg
为空,则仅对bool_expr
进行判断,然后根据判断结果执行第一条判断为真的表达式对应pl_stmt_list
。bool_expr
:布尔表达式。ELSE
:默认操作,若无匹配成功的 WHEN 子句,则会执行 ELSE 后的pl_stmt_list
。
注意
- NULL 值处理:CASE 语句中,WHEN NULL 不会匹配 NULL 值,需使用 IS NULL。
- 选择与搜索:CASE 语句若指定了case_arg,则为选择型,使用一个选择器,其值用于选择几个备选方案之一;若未指定 case_arg,则为搜索型,它的 WHEN 子句包含产生布尔值的搜索条件。
- 条件顺序:IF-THEN-ELSE 和 CASE 语句都会按顺序评估条件,一旦满足某个条件,后续条件将被忽略。
- 异常处理:条件表达式中若出现异常,会终止整个条件语句。
1.5 条件语句示例
示例1
IF-THEN-ELSE使用演示。
sqlSQL> DECLARE v_age NUMBER := 25; BEGIN IF v_age < 18 THEN DBMS_OUTPUT.PUT_LINE('未成年人'); ELSIF v_age >= 18 AND v_age < 65 THEN DBMS_OUTPUT.PUT_LINE('成年人'); ELSE DBMS_OUTPUT.PUT_LINE('老年人'); END IF; END; / --输出 成年人
示例2
嵌套 IF 语句使用演示。
sqlSQL> DECLARE salary NUMBER := 60; department VARCHAR := 'IT'; bonus NUMBER; BEGIN IF salary > 50 THEN IF department = 'IT' THEN bonus := salary * 0.2; ELSE bonus := salary * 0.15; END IF; ELSE bonus := salary * 0.1; END IF; DBMS_OUTPUT.PUT_LINE(bonus); END; / --输出 12
示例3
选择型 CASE 语句使用演示。
sqlSQL> DECLARE v_dept_id NUMBER := 20; v_dept_name VARCHAR2(50); BEGIN v_dept_name := CASE v_dept_id WHEN 10 THEN '人力资源部' WHEN 20 THEN '财务部' WHEN 30 THEN '市场部' ELSE '未知部门' END; DBMS_OUTPUT.PUT_LINE('部门名称: ' || v_dept_name); END; / --输出 部门名称: 财务部
示例4
搜索型 CASE 语句使用演示。
sqlSQL> DECLARE v_salary NUMBER := 80; v_level VARCHAR2(20); BEGIN v_level := CASE WHEN v_salary < 50 THEN '低级' WHEN v_salary >= 50 AND v_salary < 100 THEN '中级' WHEN v_salary >= 100 THEN '高级' ELSE '未知' END; DBMS_OUTPUT.PUT_LINE('等级: ' || v_level); END; / --输出 等级: 中级
二、循环语句
在 PL/SQL 里,循环语句能够让代码块重复执行。下面介绍四种主要的循环结构(LOOP、WHILE、FOR、FORALL)及其使用方式。
- LOOP:无条件循环,最基础的循环结构,它会一直执行代码块,直到遇到 EXIT 语句;
- WHILE:条件判断在前,在每次执行循环体之前,都会先对条件进行检查,只有条件为真时,才会执行循环体;
- FOR:范围式循环,按照指定的次数或游标行来执行循环,循环变量会自动递增或者递减;
- FORALL:类似 FOR 循环,无游标操作,增加集合类型操作,且只能执行一个 DML 语句(UPDATE、INSERT、DELETE、EXECUTE)。
2.1 LOOP 语法格式
2.2 LOOP 参数说明
name_space
:结束 LOOP 语句后可带名空间(编译器不检查),用于标识具体的 LOOP 语句,通常用于嵌套的 LOOP 语句中。
2.3 WHILE 语法格式
2.4 WHILE 参数说明
bool_expr
:布尔表达式,在每次执行循环体之前,都会先对表达式条件进行检查,只有条件为真时,才会执行循环体。
2.5 FOR 语法格式
(select_with_parens::=
, pl_stmt_list::=
)
2.6 FOR 参数说明
ColumnName
:循环变量,用于存储当前的循环值,无需提前声明,会自动创建并在循环结束后销毁。REVERSE
:倒序循环关键字,后续循环定义参数必须从大到小,否则将不进入循环体。lower_bound..higher_bound
:循环起始和结束值范围,ColId 在此范围内从小到大,步长为1,每次循环自增(若有 REVERSE 关键字则步长为-1自减),直到超过结束值后,程序退出循环。IDENT
:一般为游标名,需提前声明该游标,此时 ColumnName 用于存储从游标中提取的每一行数据集,每次循环取一条,直到最后一条止。name_space
:结束 FOR LOOP 语句后可带名空间(编译器不检查),用于标识具体的 FOR LOOP 语句,通常用于嵌套的 FOR LOOP 语句中。
2.7 FORALL 语法格式
2.8 FORALL 参数说明
ColumnName
:循环记数变量,用于存储当前的循环值,无需提前声明,会自动创建并在循环结束后销毁。lower_bound..higher_bound
:循环起始和结束值范围,ColId 在此范围内从小到大,步长为1,每次循环自增,直到超过结束值后,程序退出循环。INDICES OF
:遍历集合的有效索引(适用于稀疏集合)。IDENT
:一般为集合名,需提前声明该集合,此时 ColumnName 用于存储集合的有效索引。BETWEEN lower_bound AND higher_bound
:限制有效索引的范围在 lower_bound 和 higher_bound 之间。VALUES OF
:直接遍历集合 IDENT 中的值,而非索引,目前要求集合的成员变量类型必须是整型。dml_stmt1
:一个 DML 语句,只能为 UPDATE、INSERT、DELETE、EXECUTE 其中之一。
注意
- 避免出现无限循环,LOOP 循环内必须使用 EXIT 语句退出,WHILE 循环则需检查是否更新循环变量。
2.9 循环语句示例
示例1
LOOP 语句使用演示。
sqlSQL> DECLARE v_counter NUMBER := 1; BEGIN LOOP DBMS_OUTPUT.PUT_LINE('当前数值为:' || v_counter); v_counter := v_counter + 1; EXIT WHEN v_counter > 5; -- 当v_counter大于5时,结束循环 END LOOP; END; / --输出 当前数值为:1 当前数值为:2 当前数值为:3 当前数值为:4 当前数值为:5
示例2
WHILE 语句使用演示。
sqlSQL> DECLARE v_counter NUMBER := 1; BEGIN WHILE v_counter <= 5 LOOP -- 先判断条件,再执行循环 DBMS_OUTPUT.PUT_LINE('当前数值为:' || v_counter); v_counter := v_counter + 1; END LOOP; END; / --输出 当前数值为:1 当前数值为:2 当前数值为:3 当前数值为:4 当前数值为:5
示例3
FOR 语句使用演示,数值范围型,正向和反向。
sqlSQL> BEGIN -- 正向循环,从1递增到5 FOR i IN 1..5 LOOP DBMS_OUTPUT.PUT_LINE('正向数值:' || i); END LOOP; -- 反向循环,从5递减到1 FOR i IN REVERSE 5..1 LOOP DBMS_OUTPUT.PUT_LINE('反向数值:' || i); END LOOP; END; / --输出 正向数值:1 正向数值:2 正向数值:3 正向数值:4 正向数值:5 反向数值:5 反向数值:4 反向数值:3 反向数值:2 反向数值:1
示例4
FOR 语句使用演示,游标型。
sqlSQL> CREATE TABLE tab_forcur (table_name VARCHAR2(100), table_type VARCHAR2(50)); SQL> INSERT INTO tab_forcur (table_name, table_type) VALUES ('TABLE1', 'TABLE')('TABLE2', 'TABLE')('TABLE3', 'VIEW'); SQL> DECLARE CURSOR cur IS SELECT * FROM tab_forcur; BEGIN FOR i IN cur LOOP --使用显式游标 SEND_MSG(i.table_name); END LOOP; FOR j IN (SELECT * FROM tab_forcur) LOOP --使用隐式游标 SEND_MSG(j.table_type); END LOOP; END; / -- 输出 TABLE1 TABLE2 TABLE3 TABLE TABLE VIEW
示例5
FORALL 语句使用演示,数值范围型。
sqlSQL> CREATE TABLE tab_forall (id INT, name VARCHAR2(50)); SQL> BEGIN FORALL i IN 1..5 INSERT INTO tab_forall VALUES(i,'test'||i); END; / SQL> SELECT * FROM tab_forall; +----+-------+ | ID | NAME | +----+-------+ | 1 | test1 | | 2 | test2 | | 3 | test3 | | 4 | test4 | | 5 | test5 | +----+-------+ SQL> DECLARE TYPE ty_forall IS TABLE OF tab_forall%ROWTYPE INDEX BY BINARY_INTEGER; tab_ty_forall ty_forall; BEGIN FOR i IN 1..5 LOOP tab_ty_forall(i).id:=i; tab_ty_forall(i).name:='NAME'||i; END LOOP; FORALL i IN 1..tab_ty_forall.count INSERT INTO tab_forall VALUES(tab_ty_forall(i).id,tab_ty_forall(i).name); END; / SQL> SELECT * FROM tab_forall; +----+-------+ | ID | NAME | +----+-------+ | 1 | test1 | | 2 | test2 | | 3 | test3 | | 4 | test4 | | 5 | test5 | | 1 | NAME1 | | 2 | NAME2 | | 3 | NAME3 | | 4 | NAME4 | | 5 | NAME5 | +----+-------+
- 示例6
FORALL 语句使用演示,INDICES 和 VALUES。
sqlSQL> CREATE TABLE tab_forall2(id INT); SQL> CREATE TABLE tab_forall3(id INT); SQL> DECLARE TYPE ty_forall2 IS VARRAY(100) OF INT; tab_ty_forall2 ty_forall2 := ty_forall2(1, 3, 2, 5, 3); BEGIN FORALL i IN INDICES OF tab_ty_forall2 --i取值为1,2,3,4,5 INSERT INTO tab_forall2 (id) VALUES (tab_ty_forall2(i)); FORALL i IN VALUES OF tab_ty_forall2 --i取值为1,3,2,5,3 INSERT INTO tab_forall3 (id) VALUES (tab_ty_forall2(i)); END; / SQL> SELECT * FROM tab_forall2; +----+ | ID | +----+ | 1 | | 3 | | 2 | | 5 | | 3 | +----+ SQL> SELECT * FROM tab_forall3; +----+ | ID | +----+ | 1 | | 2 | | 3 | | 3 | | 2 | +----+
三、跳转语句
在 PL/SQL 里,跳转语句能够改变程序的正常执行流程。下面介绍四种主要的跳转语句(CONTINUE、EXIT、RETURN、GOTO)及其使用方式。
- CONTINUE:通常在循环语句中使用,用于跳过本次循环的后续内容,继续下一次循环;
- EXIT:通常在循环语句中使用,用于立即退出当前循环,不再执行循环体内的剩余部分,并且不再进行下一次循环(IF THEN EXIT 同 EXIT WHEN);
- GOTO:允许程序无条件跳转到代码中的另一个标签位置,需定义 Label 标签名,PL/SQL 中对 GOTO 语句有一些限制,对于块、循环、IF 语句而言,从外层跳转到内层是非法的;
- RETURN:立刻结束当前子程序的执行,并返回结果(在存储函数中必须使用)。
3.1 CONTINUE 语法格式
3.2 CONTINUE 参数说明
CONTINUE
:目前仅可单独使用此关键字,用于跳过本次循环的后续内容,继续下一次循环。
3.3 EXIT 语法格式
3.4 EXIT 参数说明
bool_expr
:布尔表达式,当值为真时,则执行 EXIT,是 IF bool_expr THEN EXIT 的简写。
3.5 GOTO 语法格式
3.6 GOTO 参数说明
ColumnName
:标签名,可跳转到该标签指定的位置或语句;
3.7 RETURN 语法格式
3.8 RETURN 参数说明
b_expr
:返回值,在存储函数中必须有返回值,且类型需与函数定义的返回类型一致;其余 PL/SQL 子程序中也可包含 RETURN 语句,用于立即结束子程序,且返回值只能为整数,或为空则默认为 NULL。
3.9 跳转语句示例
示例1
CONTINUE 语句使用演示。
sqlSQL> BEGIN FOR i IN 1..6 LOOP IF i = 5 THEN CONTINUE; END IF; SEND_MSG('VALUE: ' || i); END LOOP; END; / --输出,可与 EXIT 结果对比理解 VALUE: 1 VALUE: 2 VALUE: 3 VALUE: 4 VALUE: 6
示例2
EXIT 语句使用演示。
sqlSQL> BEGIN FOR i IN 1..6 LOOP IF i = 5 THEN EXIT; END IF; SEND_MSG('VALUE: ' || i); END LOOP; END; / SQL> BEGIN FOR i IN 1..6 LOOP EXIT WHEN i = 5; --简写效果相同 SEND_MSG('VALUE: ' || i); END LOOP; END; / --两种写法输出相同 VALUE: 1 VALUE: 2 VALUE: 3 VALUE: 4
示例3
GOTO 语句使用演示,搭配 IF 判断语句实现循环。
sqlSQL> DECLARE v_num INT := 1; BEGIN <<loop_start>> IF v_num <= 5 THEN DBMS_OUTPUT.PUT_LINE('当前数字为: ' || v_num); v_num := v_num + 1; GOTO loop_start; END IF; END; / SQL> DECLARE v_num INT := 1; BEGIN loop_start: IF v_num <= 5 THEN DBMS_OUTPUT.PUT_LINE('当前数字为: ' || v_num); v_num := v_num + 1; GOTO loop_start; END IF; END; / --两种写法输出相同 当前数字为: 1 当前数字为: 2 当前数字为: 3 当前数字为: 4 当前数字为: 5
示例4
RETUEN 语句使用演示。
sqlSQL> CREATE FUNCTION func_sum(a INT, b INT) RETURN INT IS BEGIN IF a < 0 OR b < 0 THEN RETURN NULL; -- 参数为负数时返回 NULL END IF; RETURN a + b; -- 返回两数之和 END; / SQL> SELECT func_sum(-2,3) AS RES; +--------+ | RES | +--------+ | <NULL> | +--------+ SQL> SELECT func_sum(2,3) AS RES; +--------+ | RES | +--------+ | 5 | +--------+