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 多奇怪!多怪异!多愚蠢!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/663894.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MQTT环境搭建

本文介绍了MQTT测试环境的搭建过程和测试时使用的命令&#xff0c;对需要使用MQTT协议的相关开发人员有一定的帮助。 MQTT&#xff08;Message Queuing Telemetry Transport&#xff0c;消息队列遥测传输&#xff09;。 MQTT是 ISO 标准&#xff08;ISO/IEC PRF 20922&#xf…

国标GB/T 28181详解:GB/T28181状态信息报送流程

目 录 一、状态信息报送 二、状态信息报送的基本要求 三、命令流程 1、流程图 2、流程描述 四、协议接口 五、产品说明 六、状态信息报送的作用 七、参考 在国标GBT28181中&#xff0c;定义了状态信息报送的流程&#xff0c;当源设备(包括网关、SIP 设备、SIP 客…

ThinkPHP6的助手函数汇总

原文地址 abort(): 抛出 HTTP 异常 1. /** 2. * 抛出 HTTP 异常 3. * param integer|Response $code 状态码 或者 Response 对象实例 4. * param string $message 错误信息 5. * param array $header 参数 6. */ 7. abort($code, string…

关于可变类型和不可变类型的探究

个人猜想&#xff08;很遗憾失败了&#xff09; 在硬盘或者系统中存在一个字符集 如果存在硬盘中&#xff0c;那么硬盘出厂的时候他的字符集所占用的空间就已经确定了。 如果存在于系统的话&#xff0c;硬盘应该在出厂的时候为系统设置一个存储系统字符集的地方。在安装系统…

数据库建模之PowerDesigner创建概念模型

数据模型&#xff08;Data Model&#xff09;是数据特征的抽象&#xff0c;它从抽象层次上描述了系统的静态特征、动态行为和约束条件&#xff0c;为数据库系统的信息表示与操作提供一个抽象的框架。数据模型所描述的内容有三部分&#xff0c;分别是数据结构、数据操作和数据约…

01-16Maven-SpringBoot入门

Maven继承Maven高级SpringSpringBoot入门 Maven 一、概念及功能 概念&#xff1a;Maven是Apache软件基金会组织维护的一款专门为Java项目提供项目构建和依赖管理的工具 1.1作用&#xff1a; 项目构建 构建&#xff1a;是一个将代码从开发阶段到生产阶段的一个过程&#xf…

模拟请求ElasticSearch

Step1 安装chrome的这个插件 Step2 打开插件&#xff0c;GET的json填什么。 在IDEA的debug模式&#xff0c;走到Java代码的searchBuilder&#xff0c; 在这个searchBuilder变量里&#xff0c;对里面query变量点右侧 view按钮&#xff0c; IDEA里会显示出一个json&#xff…

Redis核心技术与实战【学习笔记】 - 16.Redis 缓存异常:缓存和数据库不一致

概述 只要使用 Redis 缓存&#xff0c;就必须面对缓存和数据库的一致性问题。 重要的是&#xff0c;如果数据不一致&#xff0c;那么业务应用从缓存中读取的数据就不是最新数据&#xff0c;这会导致严重的问题。比如说&#xff0c;我们把电商商品的库存信息缓存在 Redis 中&am…

Datax问题记录

1、同步mysql&#xff1a;OS errno 24 - Too many open files 2023-11-20 12:30:04.371 [job-0] ERROR JobContainer - Exception when job run com.alibaba.datax.common.exception.DataXException: Code:[DBUtilErrorCode-07], Description:[读取数据库数据失败. 请检查您的…

1.27马尔科夫链,抽样蒙特卡洛模拟(逆转化方法,接受拒绝矩阵),马尔科夫链蒙特卡洛MCMC,隐马尔科夫(HMM(V算法剪枝优化),NLP)

马尔科夫链 蒙特卡洛法模拟 抽样&#xff0c;逆转换方法 就是说由系统自带的随机函数RANDOM&#xff0c;通过下面这个方法&#xff0c;可以变为对应的随机模拟函数 就是说要实现蒙特卡洛模拟&#xff0c;是要先有一个概率表达式&#xff0c;然后基于这个概率表达式&#xff0…

网络原理TCP/IP(1)

文章目录 端口号UDP协议 在网络通信中&#xff0c;协议非常重要 协议进行了分层 应用层就是对应着应用程序&#xff0c;是程序员打交道最多的这一层&#xff0c;调用系统提供的网络api写出来的代码都是属于应用层的 应用层有很多现成的协议&#xff0c;但是更多的还是程序员需要…

OpenHarmony—Hap包签名工具

概述 为了保证OpenHarmony应用的完整性和来源可靠&#xff0c;在应用构建时需要对应用进行签名。经过签名的应用才能在真机设备上安装、运行、和调试。developtools_hapsigner仓 提供了签名工具的源码&#xff0c;包含密钥对生成、CSR文件生成、证书生成、Profile文件签名、Ha…

C++入门【37-C++ 拷贝构造函数】

拷贝构造函数是一种特殊的构造函数&#xff0c;它在创建对象时&#xff0c;是使用同一类中之前创建的对象来初始化新创建的对象。拷贝构造函数通常用于&#xff1a; 通过使用另一个同类型的对象来初始化新创建的对象。复制对象把它作为参数传递给函数。复制对象&#xff0c;并…

2022美国大学生数学建模(优秀获奖论文)-A题:Power Planning Model: Magic Weapon for Cyclists(续)

目录 5.4.3 Multivariate nonlinear programming 5.5 Model Solving and Evaluation 5.5.1 Model Solution 5.5.2 Model Solution Method Evaluation 6 Application

前端输入框简单实现检测@成员输入

大体逻辑是 给input框添加一个input监听&#xff0c;并判断输入是否为获取当前光标的位置&#xff0c;你输入的肯定在光标之前&#xff0c;且肯定是最后一个input输入的内容换行可以被认为空格&#xff0c;需要进行全局替换判断成功的逻辑分为两部分&#xff0c;前方一般来说是…

EF Core入门例子(以SqLite为数据库)

测试环境&#xff1a; visual studio 2017 .net core 2.1 具体步骤如下&#xff1a; 1 新增名称为EFCoreDemo的.net core控制台程序&#xff0c;版本选择.net core 2.1&#xff0c;项目不能放到带中文的目录下&#xff0c;不然到后面执行Add-Migration命令时会报如下的错误…

Jetpack Compose系列(1)-初识Jetpck

Jetpack Compose是什么 2019年的I/O大会上&#xff0c;Google宣布Kotlin成为Android开发首选语言&#xff08;这次不是第一次说了&#xff09;&#xff0c;且后续会有新的Jetpack API和功能将在Kotlin中提供&#xff0c;并同时开源Jetpack Compose。 简介 Jetpack是一套库、…

AJAX-URL查询参数

定义&#xff1a;浏览器提供给服务器的额外信息&#xff0c;让服务器返回浏览器想要的数据 http://xxxx.com/xxx/xxx?参数名1值1&参数名2值2 axios语法 使用axios提供的params选项 注意&#xff1a;axios在运行时把参数名和值&#xff0c;会拼接到url?参数名值 axios(…

C++学习Day01之C++对C语言增强和扩展

目录 一、程序及输出1.1 全局变量检测增强1.2 函数检测增强1.3 类型转换检测增强1.4 struct增强1.5 bool类型扩展1.6 三目运算符增强1.7 const增强1.7.1 全局Const对比1.7.2 局部Const对比1.7.3 Const变量初始化数组1.7.3 Const修饰变量的链接性 二、分析总结 一、程序及输出 …

【Java开发岗面试】八股文—微服务、消息中间件

声明&#xff1a; 背景&#xff1a;本人为24届双非硕校招生&#xff0c;已经完整经历了一次秋招&#xff0c;拿到了三个offer。本专题旨在分享自己的一些Java开发岗面试经验&#xff08;主要是校招&#xff09;&#xff0c;包括我自己总结的八股文、算法、项目介绍、HR面和面试…