名校建设网站,前沿设计公司网站,天元建设集团有限公司张国庆,wordpress图片延迟加载插件总的目录和进度#xff0c;请参见开始读 Oracle PL/SQL Programming 第6版
本章探讨 PL/SQL 的迭代控制结构#xff08;也称为循环#xff09;#xff0c;它允许您重复执行相同的代码。
PL/SQL 提供了三种不同类型的循环结构#xff1a;
简单或无限循环FOR 循环#x…总的目录和进度请参见开始读 Oracle PL/SQL Programming 第6版
本章探讨 PL/SQL 的迭代控制结构也称为循环它允许您重复执行相同的代码。
PL/SQL 提供了三种不同类型的循环结构
简单或无限循环FOR 循环数字和游标WHILE 循环
每种类型的循环都是针对特定目的而设计
属性描述循环如何终止循环重复执行代码。 如何使循环停止执行其主体何时进行终止测试终止测试是在循环的开始还是结束时进行 后果是什么使用此循环的原因您应该考虑哪些特殊因素来确定此循环是否适合您的情况
Loop Basics
为什么存在三种不同类型的循环 为了给您提供灵活性为了代码简单更容易理解和维护。
Examples of Different Loops
简单或无限循环
LOOPEXIT WHEN i 100;...;i: i 1;
END LOOP;FOR循环数字
FOR i IN 1 .. 100
LOOP...;
END LOOP;FOR循环游标
FOR i IN (SELECT ...)
LOOP...;
END LOOP;WHILE循环
WHILE (i 100)
LOOP...;
END LOOP;Structure of PL/SQL Loops
虽然这三个循环结构之间存在差异但每个循环都有两个部分 循环边界 它由启动循环的保留字、导致循环终止的条件以及结束循环的 END LOOP 语句组成。 循环体 这是循环边界内的可执行语句序列在循环的每次迭代中执行。
The Simple Loop
形式为
LOOP执行语句...
END LOOP;仅当执行语句包含EXIT或EXIT WHEN时才会退出循环否则为无限循环。
EXIT;
EXIT WHEN 判断条件;EXIT WHEN等同于IF-THEN加EXIT。
使用简单循环的场景
希望循环至少执行1次无法确定循环需执行多少次
当存在单个条件表达式来确定循环是否应终止时最好使用 EXIT WHEN。 在有多个退出条件的情况下或者当您需要根据不同的条件设置从循环中退出时最好使用 IF 或 CASE 语句然后加上EXIT 语句 。
Emulating a REPEAT UNTIL Loop
PL/SQL中没有WHILE…UNTIL语句但有类似的实现
LOOP...EXIT WHEN ...;
END LOOP;The Intentionally Infinite Loop
无限循环可能存在但通常都会加sleep语句
LOOP 执行操作;DBMS_LOCK.sleep(10);
END LOOP;如何终止无限循环在交互式PL/SQL中可以用“CtrlC”在后台运行的程序可以用操作系统的kill命令注意数据库中的ALTER SYSTEM KILL SESSION命令不一定可以终止无限循环。但是kill命令有可能误杀例如在共享服务器模式下。
作者推荐的方式为利用PL/SQL中的管道这类似于Shell编程中的信号
DECLAREpipename CONSTANT VARCHAR2(12) : signaler;result INTEGER;pipebuf VARCHAR2(64);
BEGIN/* create private pipe with a known name */result : DBMS_PIPE.create_pipe(pipename);LOOPDBMS_OUTPUT.PUT_LINE(Im doing works ...); -- 请替换为实际执行的操作DBMS_LOCK.sleep(5);/* see if there is a message on the pipe */IF DBMS_PIPE.receive_message(pipename, 0) 0THEN/* interpret the message and act accordingly */DBMS_PIPE.unpack_message(pipebuf);IF pipebuf stopTHENDBMS_OUTPUT.PUT_LINE(Exiting ...);EXIT;END IF;END IF;END LOOP;
END;上面是接收信号的程序下面是发送信号的程序
DECLAREpipename VARCHAR2 (12) : signaler;result INTEGER : DBMS_PIPE.create_pipe (pipename);
BEGINDBMS_PIPE.pack_message (stop);result : DBMS_PIPE.send_message (pipename);
END;测试在SQL Plus中通过但SQL Developer没有通过不知为何。 以下为成功时的输出
Im doing works ...
Im doing works ...
Im doing works ...
Im doing works ...
Im doing works ...
Exiting ...PL/SQL procedure successfully completed.此示例使用私有管道因此发送和接收程序需为同一用户。 另请注意私有管道的数据库命名空间在当前用户运行的所有会话中是全局的。
The WHILE Loop
如事先不知道循环执行的次数可以一次都不执行则可使用 WHILE 循环
WHILE 布尔变量|表达式
LOOP执行语句
END LOOP;The Numeric FOR Loop
PL/SQL FOR 循环有两种数字FOR 循环和游标FOR 循环。 数字 FOR 循环是传统且熟悉的“计数”循环。 FOR 循环的迭代次数在循环开始时就已知。
范围方案隐式声明循环索引如果尚未声明指定范围的起点和终点并可选择指定循环索引进行的顺序从最低到最高或从最高到最低。
FOR loop index IN [REVERSE] lowest number .. highest number
LOOPexecutable statement(s)
END LOOP;Rules for Numeric FOR Loops
不要声明循环索引。 PL/SQL 自动且隐式地将其声明为数据类型为 INTEGER 的局部变量。 该索引的范围是循环本身 您不能在循环外引用循环索引。范围方案中使用的表达式最低和最高边界在循环开始时评估一次。 在循环执行期间不会重新评估范围。所以后面改了也不会生效。切勿在循环内更改循环索引或范围边界的值。
Examples of Numeric FOR Loops
一个倒计时程序
BEGIN
FOR i IN REVERSE 1 .. 10
LOOPDBMS_OUTPUT.PUT_LINE(i);
END LOOP;
END;还有范围可以由常数定义也可以由变量或表达式定义。
Handling Nontrivial Increments
和C语言不一样PL/SQL中的循环索引的步增/步减永远是1。所以如果增减量为非1则需要另加IF判断如MOD函数。
The Cursor FOR Loop
游标 FOR 循环是与直接合并在循环边界内的显式游标或 SELECT 语句关联并实际由其定义的循环。 仅当需要从游标中获取并处理每条记录时游标经常出现这种情况才使用游标 FOR 循环。
游标 FOR 循环充分利用了过程结构与 SQL 数据库语言的强大功能的紧密且有效的集成。 它减少了从游标获取数据所需编写的代码量。 它大大减少了在编程中引入循环错误的机会而循环是程序中最容易出错的部分之一。
FOR record IN { cursor_name | (explicit SELECT statement) }
LOOPexecutable statement(s)
END LOOP;其中 record 是由 PL/SQL 使用 %ROWTYPE 属性针对cursor_name 指定的游标隐式声明的记录。
不要显式声明与循环索引记录同名的记录。 它不是必需的PL/SQL 隐式声明它在循环中使用并且可能导致逻辑错误。 有关在循环执行之外或之后访问有关游标 FOR 循环记录的信息的提示请看本文后面部分Obtaining Information About FOR Loop Execution。
Example of Cursor FOR Loops
没有用游标FOR循环之前
SET SERVEROUTPUT ON
DECLARECURSOR hr_cur ISSELECT first_name, last_nameFROM employees WHERE department_id 100;hr_rec hr_cur%ROWTYPE;
BEGINOPEN hr_cur;LOOPFETCH hr_cur INTO hr_rec;EXIT WHEN hr_cur%NOTFOUND;DBMS_OUTPUT.PUT_LINE(hr_rec.first_name || || hr_rec.last_name);END LOOP;CLOSE hr_cur;END;用游标FOR循环后简洁很多 无需记录的声明。 OPEN、FETCH 和 CLOSE 语句已消失。 不再需要检查 %NOTFOUND 属性。
SET SERVEROUTPUT ON
DECLARECURSOR hr_cur ISSELECT first_name, last_nameFROM employees WHERE department_id 100;
BEGINFOR hr_rec IN hr_curLOOPDBMS_OUTPUT.PUT_LINE(hr_rec.first_name || || hr_rec.last_name);END LOOP;END;
与所有其他游标一样您可以在游标 FOR 循环中将参数传递给游标。 如果光标选择列表中的任何列是表达式请记住您必须在选择列表中为该表达式指定别名。 在循环内访问游标记录中特定值的唯一方法是使用点符号record_name.column_name如 ocupancy_rec.room_number因此您需要一个与表达式关联的列名。
Loop Labels
您可以使用标签为循环命名。 格式为
label_namelabel_name 必须出现在循环体第一个语句之前而在END LOOP也可以加label_name但不是必须。
循环标签作用
代码更容易维护和调试。就好像括号必须成对出现。可使用标签来限定循环索引变量的名称这有助于提高可读性。当您有嵌套循环时您可以使用标签来提高可读性并增强对循环执行的控制。如EXIT loop_label [WHEN condition];不过这种用户和GOTO一样不建议。
The CONTINUE Statement
CONTINUE 语句退出循环的当前迭代并立即继续该循环的下一次迭代。 该语句有两种形式就像 EXIT 一样无条件 CONTINUE 和条件 CONTINUE WHEN。
下例演示了利用CONTINUE实现步进为3
BEGINFOR l_index IN 1 .. 10LOOPCONTINUE WHEN MOD (l_index - 1, 3) ! 0;DBMS_OUTPUT.PUT_LINE (Loop index || TO_CHAR (l_index));END LOOP;
END;
/输出为
Loop index 1
Loop index 4
Loop index 7
Loop index 10您还可以使用 CONTINUE 终止内部循环并立即继续进行外部循环体的下一次迭代。 为此您需要使用标签为外部循环命名。
IS CONTINUE AS BAD AS GOTO?
continue 语句不能滥用但用对地方则很有价值因为它使代码更短使代码更易于阅读并减少了对布尔变量的需求。
作者举了使用和不使用continue的2个例子作为对比。我看懂了并认可。
使用continue的例子
LOOPEXIT WHEN exit_condition_met;CONTINUE WHEN condition1;CONTINUE WHEN condition2;setup_steps_here;IF condition4 THENaction4_executed;CONTINUE;END IF;IF condition5 THENaction5_executed;CONTINUE; -- Not strictly required.END IF;
END LOOP;如果不使用continue:
LOOPEXIT WHEN exit_condition_met;IF condition1THENNULL;ELSIF condition2THENNULL;ELSEsetup_steps_here;IF condition4 THENaction4_executed;ELSIF condition5 THENaction5_executed;END IF;END IF;
END LOOP;Tips for Iterative Processing
循环是非常强大且有用的结构但您应该谨慎使用它们。 程序中的性能问题通常可以追溯到循环并且循环中的任何问题都会因其重复执行而被放大。 确定何时停止循环的逻辑可能非常复杂。 本节提供了一些关于如何编写干净、易于理解且易于维护的循环的技巧。
Use Understandable Names for Loop Indexes
使用有意义的循环索引变量名称而非简单的ijk。
The Proper Way to Say Goodbye
结构化编程的一个重要且基本的原则是“一进一出” 也就是说程序应该有一个入口点和一个出口点。一个入口是必然的这里讲的是如何避免多个出口。
您应该遵循以下循环终止准则
不要在 FOR 和 WHILE 循环中使用 EXIT 或 EXIT WHEN 语句。不要在循环中使用 RETURN 或 GOTO 语句这同样会导致循环过早、非结构化终止。
如果需要根据游标 FOR 循环获取的信息终止循环例如当取得值的合计大于某值时退出则应使用 WHILE 循环或简单循环代替。 那么代码的结构就会更清楚地表达你的意图。
Obtaining Information About FOR Loop Execution
FOR 循环是方便且简洁的结构对于游标 FOR 循环尤其如此。 然而有一个权衡数据库自动为您完成大量工作但您在循环终止后对有关循环最终结果的信息的访问受到限制。
简单来说游标 FOR 循环的END LOOP语句后游标就被关闭了也就是说此时无法获取游标的信息。因此你需要再循环内部游标关闭前暂存游标的信息如行数cursor%ROWCOUNT后续关闭后就可以继续访问。
SQL Statement as Loop
实际上您可以将像 SELECT 这样的 SQL 语句视为循环。 毕竟这样的语句指定了对一组数据采取的操作 然后SQL 引擎“循环”数据集并应用操作。
例如一个数据归档的例子从源表中逐行读取然后插入归档表后删除。这既可以用PL/SQL实现也可以用2条SQL实现INSERT INTO … DELETEDELETE
SQL实现编写的代码更少而且运行效率更高因为减少了上下文切换的次数在 PL/SQL 和 SQL 执行引擎之间来回移动。 只执行一次插入和一次删除。
但SQL的灵活性差一点因为SQL是事务型的要么全成功要么全失败SQL也不能做特殊处理如记录归档失败的记录。因此PL/SQL 提供更大的灵活性。
总之PL/SQL 提供一次访问和处理单行并采取操作或许还有基于该特定记录内容的复杂过程逻辑的能力。 另一方面使用原生SQL代码更少运行效率更高。必要时可以混合使用 PL/SQL 和 SQL。
单词
go figure 多奇怪多怪异多愚蠢