Oracle PL/SQL Programming 第5章:Iterative Processing with Loops 读书笔记

本文主要是介绍Oracle PL/SQL Programming 第5章:Iterative Processing with Loops 读书笔记,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

总的目录和进度,请参见开始读 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中可以用“Ctrl+C”,在后台运行的程序可以用操作系统的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('I''m 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 = 'stop'THENDBMS_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没有通过,不知为何。
以下为成功时的输出:

I'm doing works ...
I'm doing works ...
I'm doing works ...
I'm doing works ...
I'm 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_name>>

<<label_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

使用有意义的循环索引变量名称,而非简单的i,j,k。

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 … DELETE,DELETE )

SQL实现编写的代码更少,而且运行效率更高,因为减少了上下文切换的次数(在 PL/SQL 和 SQL 执行引擎之间来回移动)。 只执行一次插入和一次删除。

但SQL的灵活性差一点,因为SQL是事务型的,要么全成功,要么全失败;SQL也不能做特殊处理,如记录归档失败的记录。因此,PL/SQL 提供更大的灵活性。

总之,PL/SQL 提供一次访问和处理单行并采取操作(或许还有基于该特定记录内容的复杂过程逻辑)的能力。 另一方面,使用原生SQL代码更少,运行效率更高。必要时,可以混合使用 PL/SQL 和 SQL。

单词

  • go figure 多奇怪!多怪异!多愚蠢!

这篇关于Oracle PL/SQL Programming 第5章:Iterative Processing with Loops 读书笔记的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/673470

相关文章

SQL中的外键约束

外键约束用于表示两张表中的指标连接关系。外键约束的作用主要有以下三点: 1.确保子表中的某个字段(外键)只能引用父表中的有效记录2.主表中的列被删除时,子表中的关联列也会被删除3.主表中的列更新时,子表中的关联元素也会被更新 子表中的元素指向主表 以下是一个外键约束的实例展示

基于MySQL Binlog的Elasticsearch数据同步实践

一、为什么要做 随着马蜂窝的逐渐发展,我们的业务数据越来越多,单纯使用 MySQL 已经不能满足我们的数据查询需求,例如对于商品、订单等数据的多维度检索。 使用 Elasticsearch 存储业务数据可以很好的解决我们业务中的搜索需求。而数据进行异构存储后,随之而来的就是数据同步的问题。 二、现有方法及问题 对于数据同步,我们目前的解决方案是建立数据中间表。把需要检索的业务数据,统一放到一张M

如何去写一手好SQL

MySQL性能 最大数据量 抛开数据量和并发数,谈性能都是耍流氓。MySQL没有限制单表最大记录数,它取决于操作系统对文件大小的限制。 《阿里巴巴Java开发手册》提出单表行数超过500万行或者单表容量超过2GB,才推荐分库分表。性能由综合因素决定,抛开业务复杂度,影响程度依次是硬件配置、MySQL配置、数据表设计、索引优化。500万这个值仅供参考,并非铁律。 博主曾经操作过超过4亿行数据

性能分析之MySQL索引实战案例

文章目录 一、前言二、准备三、MySQL索引优化四、MySQL 索引知识回顾五、总结 一、前言 在上一讲性能工具之 JProfiler 简单登录案例分析实战中已经发现SQL没有建立索引问题,本文将一起从代码层去分析为什么没有建立索引? 开源ERP项目地址:https://gitee.com/jishenghua/JSH_ERP 二、准备 打开IDEA找到登录请求资源路径位置

MySQL数据库宕机,启动不起来,教你一招搞定!

作者介绍:老苏,10余年DBA工作运维经验,擅长Oracle、MySQL、PG、Mongodb数据库运维(如安装迁移,性能优化、故障应急处理等)公众号:老苏畅谈运维欢迎关注本人公众号,更多精彩与您分享。 MySQL数据库宕机,数据页损坏问题,启动不起来,该如何排查和解决,本文将为你说明具体的排查过程。 查看MySQL error日志 查看 MySQL error日志,排查哪个表(表空间

MySQL高性能优化规范

前言:      笔者最近上班途中突然想丰富下自己的数据库优化技能。于是在查阅了多篇文章后,总结出了这篇! 数据库命令规范 所有数据库对象名称必须使用小写字母并用下划线分割 所有数据库对象名称禁止使用mysql保留关键字(如果表名中包含关键字查询时,需要将其用单引号括起来) 数据库对象的命名要能做到见名识意,并且最后不要超过32个字符 临时库表必须以tmp_为前缀并以日期为后缀,备份

[MySQL表的增删改查-进阶]

🌈个人主页:努力学编程’ ⛅个人推荐: c语言从初阶到进阶 JavaEE详解 数据结构 ⚡学好数据结构,刷题刻不容缓:点击一起刷题 🌙心灵鸡汤:总有人要赢,为什么不能是我呢 💻💻💻数据库约束 🔭🔭🔭约束类型 not null: 指示某列不能存储 NULL 值unique: 保证某列的每行必须有唯一的值default: 规定没有给列赋值时的默认值.primary key:

MySQL-CRUD入门1

文章目录 认识配置文件client节点mysql节点mysqld节点 数据的添加(Create)添加一行数据添加多行数据两种添加数据的效率对比 数据的查询(Retrieve)全列查询指定列查询查询中带有表达式关于字面量关于as重命名 临时表引入distinct去重order by 排序关于NULL 认识配置文件 在我们的MySQL服务安装好了之后, 会有一个配置文件, 也就

Java 连接Sql sever 2008

Java 连接Sql sever 2008 /Sql sever 2008 R2 import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.Statement; public class TestJDBC

Mysql BLOB类型介绍

BLOB类型的字段用于存储二进制数据 在MySQL中,BLOB类型,包括:TinyBlob、Blob、MediumBlob、LongBlob,这几个类型之间的唯一区别是在存储的大小不同。 TinyBlob 最大 255 Blob 最大 65K MediumBlob 最大 16M LongBlob 最大 4G