📄 第一章 pl-sql一览 - pl-sql用户指南与参考 - whatiswhat.htm
字号:
<UL>
<LI>集合 </LI></UL>
<P>集合类型TABLE和VARRAY可以让我们声明索引表、嵌套表和变长数组(略称varray)。集合是类型相同的元素有序组合。在集合中,每个元素都有唯一一个能够确定该元素在集合中位置的下标索引。下面是嵌套表的一个例子:
</P>
<BLOCKQUOTE>
<TABLE>
<TBODY>
<TR>
<TD
noWrap><STRONG>DECLARE</STRONG><BR> <STRONG>TYPE</STRONG> staff <STRONG>IS</STRONG> <STRONG>TABLE</STRONG> <STRONG>OF</STRONG> employee;<BR><BR> staffer employee;<BR><BR> <STRONG>FUNCTION</STRONG> new_hires(hiredate <STRONG>DATE</STRONG>)<BR> <STRONG>RETURN</STRONG> staff <STRONG>IS</STRONG><BR> <STRONG>BEGIN</STRONG><BR> ...<BR> <STRONG>END</STRONG>;<BR><STRONG>BEGIN</STRONG><BR> staffer := new_hires(<EM>'10-NOV-98'</EM>)(5);<BR> ...<BR><STRONG>END</STRONG>;
</TD></TR></TBODY></TABLE></BLOCKQUOTE>
<P>集合有些像三代语言中的数组,并且可以作为参数进行传递。 </P>
<UL>
<LI>记录 </LI></UL>
<P>我们知道,可以使用%ROWTYPE属性获取数据表中一行的记录类型,其实我们还可以定义自己的记录类型。
</P>
<P>记录包含名称不可重复的域,域可以有不同的数据类型。假设我们设计了一个雇员记录类型,其中有名字、工资和雇用日期,这些项虽然类型不同,但逻辑上都是相关联的。看一下下面的例子:
</P>
<BLOCKQUOTE>
<TABLE>
<TBODY>
<TR>
<TD
noWrap><STRONG>TYPE</STRONG> timerec <STRONG>IS</STRONG> <STRONG>RECORD</STRONG>(<BR> hours <STRONG>SMALLINT</STRONG>,<BR> minutes <STRONG>SMALLINT</STRONG><BR>);<BR><BR><STRONG>TYPE</STRONG> meetingtyp <STRONG>IS</STRONG> <STRONG>RECORD</STRONG>(<BR> date_held <STRONG>DATE</STRONG>,<BR> DURATION timerec, <EM>-- nested record</EM><BR> LOCATION <STRONG>VARCHAR2</STRONG>(20),<BR> purpose <STRONG>VARCHAR2</STRONG>(50)<BR>);
</TD></TR></TBODY></TABLE></BLOCKQUOTE>
<P>这里要注意的是,记录里可以嵌套记录类型。也就是说,记录本身也可以作为另一个记录的组成部分。 </P>
<UL>
<LI>对象类型 </LI></UL>
<P>PL/SQL中的面向对象编程是基于对象类型的。对象类型把数据和用于数据操作的函数和过程封装起来。其中,对象类型中的变量称为属性,函数和过程称为方法。对象类型是把大系统划分成多个逻辑实体来降低问题的复杂度,这就能使我们创建模块化、可维护和重用性好的组件了。我们在用CREATE
TABLE定义对象类型的时候,常常是创建一个对真实世界对象的抽象的模板。如下面的银行账户例子中显示,模板只指定了应用程序的环境中会使用到的属性和方法:
</P>
<BLOCKQUOTE>
<TABLE>
<TBODY>
<TR>
<TD
noWrap><STRONG>CREATE</STRONG> <STRONG>TYPE</STRONG> bank_account <STRONG>AS</STRONG> OBJECT(<BR> acct_number <STRONG>INTEGER</STRONG>(5),<BR> balance <STRONG>REAL</STRONG>,<BR> status <STRONG>VARCHAR2</STRONG>(10),<BR> MEMBER <STRONG>PROCEDURE</STRONG> <STRONG>OPEN</STRONG>(amount <STRONG>IN</STRONG> <STRONG>REAL</STRONG>),<BR> MEMBER <STRONG>PROCEDURE</STRONG> verify_acct(num <STRONG>IN</STRONG> <STRONG>INTEGER</STRONG>),<BR> MEMBER <STRONG>PROCEDURE</STRONG> <STRONG>CLOSE</STRONG>(num <STRONG>IN</STRONG> <STRONG>INTEGER</STRONG>, amount <STRONG>OUT</STRONG> <STRONG>REAL</STRONG>),<BR> MEMBER <STRONG>PROCEDURE</STRONG> deposit(num <STRONG>IN</STRONG> <STRONG>INTEGER</STRONG>, amount <STRONG>IN</STRONG> <STRONG>REAL</STRONG>),<BR> MEMBER <STRONG>PROCEDURE</STRONG> withdraw(num <STRONG>IN</STRONG> <STRONG>INTEGER</STRONG>, amount <STRONG>IN</STRONG> <STRONG>REAL</STRONG>),<BR> MEMBER <STRONG>FUNCTION</STRONG> curr_bal(num <STRONG>IN</STRONG> <STRONG>INTEGER</STRONG>)<BR> <STRONG>RETURN</STRONG> <STRONG>REAL</STRONG><BR>);
</TD></TR></TBODY></TABLE></BLOCKQUOTE>
<P>运行时,当数据结构被赋值之后,我们就可以创建抽象的银行账户了。我们可以按照需求创建任意个实例(称为对象)。每个对象都有账号,余额和状态。
</P>
<P class=title2>10、信息隐藏</P>
<P>有了信息隐藏,我们就可以只关心给定的设计级别的算法和数据结构设计。信息隐藏能把高阶的设计决定从频繁改变的低阶设计细节分离出来。
</P>
<UL>
<LI>算法 </LI></UL>
<P>我们可以通过自顶而下(top-down)的设计来实现算法隐藏。一旦我们明确了低阶过程的实现目的并定义好相应的接口说明,就可以忽略实现细节部分。例如,我们只需要知道将一个雇员的工资金额传递给过程raise_salary就可以提高该雇员的工资。任何对raise_salary方法的变动,对于应用程序来说,都是透明的。
</P>
<UL>
<LI>数据结构 </LI></UL>
<P>我们可以通过数据封装来实现信息隐藏。开发一组操作数据结构的工具子程序,就可以让使用它的用户和开发人员分离。这样一来,开发人员只需了解如何使用这些子程序来操作数据,并不需要知道数据真正的含义。
</P>
<P>使用PL/SQL包,我们就可以指定哪些子程序是公有哪些是私有,更好的提供封装,简化维护。 </P>
<P class=title2>11、错误控制</P>
<P>PL/SQL能够轻松的发现并处理预定义和用户定义的错误条件(即异常)。错误发生时,异常就会被抛出。也就是说,正常的执行会终止,程序控制权将交给PL/SQL块或子程序的异常处理部分。为控制被抛出的异常,我们需要单独编写异常控制句柄(即异常控制程序)。
</P>
<P>预定义异常会被系统隐式地抛出,例如,用一个数字除以零,PL/SQL就会自动抛出预定义异常ZERO_DIVIDE。对于用户自定义异常,必须由我们显式地使用RAISE语句抛出。
</P>
<P>我们可以在任何PL/SQL块或子程序的声明部分定义自己的异常。在执行部分,我们检查那些需要特别对待的条件,如果错误条件满足,就可以使用
RAISE抛出异常。在下面的例子中,我们要计算售货员的奖金。奖金的多少取决于他的工资(salary)和佣金(commission)。所以,如果佣金为空的话,我们就要抛出异常comm_missing。
</P>
<BLOCKQUOTE>
<TABLE>
<TBODY>
<TR>
<TD
noWrap><STRONG>DECLARE</STRONG><BR> ...<BR> comm_missing <STRONG>EXCEPTION</STRONG>; <EM>-- declare exception</EM><BR><STRONG>BEGIN</STRONG><BR> ...<BR> <STRONG>IF</STRONG> commission <STRONG>IS</STRONG> <STRONG>NULL</STRONG> <STRONG>THEN</STRONG><BR> <STRONG>RAISE</STRONG> comm_missing; <EM>-- raise exception</EM><BR> <STRONG>END</STRONG> <STRONG>IF</STRONG>;<BR><BR> bonus := (salary * 0.10) +(commission * 0.15);<BR><STRONG>EXCEPTION</STRONG><BR> <STRONG>WHEN</STRONG> comm_missing <STRONG>THEN</STRONG> <EM>-- process the exception</EM><BR> ...<BR><STRONG>END</STRONG>;
</TD></TR></TBODY></TABLE></BLOCKQUOTE>
<P class=title1>二、PL/SQL架构</P>
<P>PL/SQL的编码和运行时系统是一项技术,而不是一个独立的产品。可以把这项技术想象成一个能够编译并运行PL/SQL块和子程序的引擎。这个引擎可以安装在Oracle服务器上或安装在Oracle
Forms,Oracle Reports这样的开发工具中。所以,PL/SQL可以在两种环境中存在: </P>
<OL>
<LI>Oracle数据库服务器
<LI>Oracle开发工具 </LI></OL>
<P>这两种环境是独立的。PL/SQL虽被绑定到Oracle服务器上,但在某些工具中是无法使用的。在这两种环境下,PL/SQL引擎都能接受有效的
PL/SQL块或子程序。下图是PL/SQL引擎处理匿名块的过程,引擎会处理过程化语句,而把SQL语句发送给Oracle服务器端的SQL语句执行程序(SQL
Statement Executor)来处理。 </P><IMG alt=""
src="第一章 PL-SQL一览 - PL-SQL用户指南与参考 - whatiswhat.files/o_1-4.gif"
name="">
<P class=title2>1、Oracle数据库中</P>
<P>缺乏本地PL/SQL引擎的应用开发工具就必须依赖于Oracle来处理PL/SQL块和子程序。Oracle服务器除了能够处理SQL语句外,还会处理PL/SQL块和子程序,它会将块与子程序传给它本地的PL/SQL引擎。
</P>
<UL>
<LI>匿名块 </LI></UL>
<P>匿名PL/SQL块能被嵌到Oracle预编译程序(Oracle
Precompiler)或是OCI程序中。运行的时候,不含PL/SQL引擎的程序会把这些块发送到Oracle服务器编译并执行。
</P>
<UL>
<LI>存储过程 </LI></UL>
<P>子程序可以独立编译并存储在Oracle数据库。使用CREATE语句显式创建的子程序就是一个"存储"子程序。一旦编译并保存到数据词典中,它就成了一个模式对象(schema
object),可以被许多连到数据库的应用程序调用。 </P>
<P>定义在包内的存储子程序称为打包子程序(packaged
subprogram);单独定义的存储子程序称为独立子程序(standalone
subprogram);而在另外一个子程序或PL/SQL块内定义的存储子程序称为本地子程序,这样的子程序不能被其他应用程序调用,只供本地使用。
</P>
<P>存储过程执行效率高,耗内存少,应用集成,安全性好。例如,我们设计出一套存储过程和函数时,应用程序就可以调用这些函数和方法,这样就能避免大量的冗余代码提高效能。
</P>
<P>我们可以从数据库触发器、其他存储子程序、Oracle预编译程序、OCI程序或是SQL*Plus等,调用存储子程序。例如,我们可以像下面这样从SQL*Plus中调用独立子程序create_dept:
</P>
<BLOCKQUOTE>
<TABLE>
<TBODY>
<TR>
<TD
noWrap><STRONG>SQL</STRONG>> CALL create_dept(<EM>'FINANCE'</EM>, <EM>'NEW YORK'</EM>);
</TD></TR></TBODY></TABLE></BLOCKQUOTE>
<P>子程序以分析、编译过的形式保存在数据库中。所以,在被调用时,它们会被立即加载并传递到PL/SQL引擎。并且,它们还会利用共享内存,这样,每次只要有一个子程序需要被加载到内存,就能被多个用户调用执行。
</P>
<UL>
<LI>数据库触发器 </LI></UL>
<P>数据库触发器是与数据库中某个数据表、视图或事件相关联的存储子程序。举一个例子,我们可以让Oracle数据库在INSERT、UPDATE或
DELETE表达式影响一个表之前或之后来自动激活一个触发器。触发器的用途之一就是审核数据修改。例如,下面的表级(table-level)触发器会在emp表的salaries字段更新后被激活。
</P>
<BLOCKQUOTE>
<TABLE>
<TBODY>
<TR>
<TD
noWrap><STRONG>CREATE</STRONG> <STRONG>TRIGGER</STRONG> audit_sal<BR> AFTER <STRONG>UPDATE</STRONG> <STRONG>OF</STRONG> sal<BR> <STRONG>ON</STRONG> emp<BR> <STRONG>FOR</STRONG> EACH <STRONG>ROW</STRONG><BR><STRONG>BEGIN</STRONG><BR> <STRONG>INSERT</STRONG> <STRONG>INTO</STRONG> emp_audit<BR> <STRONG>VALUES</STRONG> ...<BR><STRONG>END</STRONG>;
</TD></TR></TBODY></TABLE></BLOCKQUOTE>
<P>触发器执行部分可以包括过程语句和SQL数据操作语句。除了表级触发器以外,还包含替代触发器(instead-of
triggers for views)和系统触发器(system-event trigger)。 </P>
<P class=title2>2、Oracle工具中</P>
<P>在包含PL/SQL引擎的条件下,应用开发工具就能够处理PL/SQL块和子程序。开发工具会把块传给它的本地PL/SQL引擎。引擎会在应用程序段执行所有的过程语句,只把SQL语句发送给Oracle。因此,大多部分工作会在应用程序端完成,而不是在服务器端。进一步说,如果块中不包含任何
SQL语句的话,这个引擎会在应用程序端执行全部的代码。 </P>
<P class=title1>三、PL/SQL的优势</
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -