plsql 用户完全手册.doc
第一章 PL/SQL一览一、理解PL/SQL的主要特性了解PL/SQL最好的方法就是从简单的实例入手。下面的程序是用于处理一个网球拍订单的。首先声明一个NUMBER类型的变量来存放现有的球拍数量。然后从数据表inventory中把球拍的数量检索出来。如果数量大于零,程序就会更新inventory表,并向purchase_record表插入一条购买记录,如果数量不大于零,程序会向purchase_record表插入一条脱销(out-of-stock)记录。 DECLARE qty_on_hand NUMBER(5);BEGIN SELECT quantity INTO qty_on_hand FROM inventory WHERE product = 'TENNIS RACKET' FOR UPDATE OF quantity; IF qty_on_hand > 0 THEN - check quantity UPDATE inventory SET quantity = quantity - 1 WHERE product = 'TENNIS RACKET' INSERT INTO purchase_record VALUES ('Tennis racket purchased', SYSDATE); ELSE INSERT INTO purchase_record VALUES ('Out of tennis rackets', SYSDATE); END IF; COMMIT;END; 在PL/SQL中,可以使用SQL语句来操作Oracle中的数据,并使用流程控制语句来处理数据。我们还可以声明常量和变量,定义函数和过程并捕获运行时错误。因此,PL/SQL是一种把SQL对数据操作的优势和过程化语言数据处理优势结合起来的语言。 1、块结构PL/SQL是一种块结构的语言,它的基本组成单元是一些逻辑块,而这些块又能嵌套任意数量子块。通常,每一个逻辑块都承担一部分工作任务,PL/SQL这种将问题分而治之(divide-and-conquer)的方法称为逐步求精(stepwise refinement)。块能够让我们把逻辑相关的声明和语句组织起来,声明的内容对于块来说是本地的,在块结构退出时它们会自动销毁。 如下图所示,一个块分为三个部分:声明,处理,异常控制。其中,只有处理部分是必需的。首先程序处理声明部分,然后被声明的内容就可以在执行部分使用,当异常发生时,就可以在异常控制部分中对抛出的异常进行捕捉、处理。我们还可以在处理部分和异常控制部分嵌套子块,但声明部分中不可以嵌套子块。不过我们仍可以在声明部分定义本地的子程序,但这样的子程序只能由定义它们的块来调用。2、变量与常量PL/SQL允许我们声明常量和变量,但是常量和变量必须是在声明后才可以使用,向前引用(forward reference)是不允许的。· 变量声明 变量可以是任何SQL类型,如CHAR,DATE或NUMBER等,也可以是PL/SQL类型,BOOLEAN或BINARY_INTEGER等。声明方法如下: part_no NUMBER(4);in_stock BOOLEAN; 我们还可以用TABLE、VARRAY和RECORD这些复合类型来声明嵌套表、变长数组(缩写为varray)和记录。 · 变量赋值 我们可以用三种方式为变量赋值,第一种,直接使用赋值操作符":=": tax := price * tax_rate;valid_id := FALSE;bonus := current_salary * 0.10;wages := gross_pay(emp_id, st_hrs, ot_hrs ) - deductions; 第二种,利用数据库中查询的结果为变量赋值: SELECT sal * 0.10 INTO bonus FROM emp WHERE empno = emp_id; 第三种,把变量作为一个OUT或IN OUT模式的参数传递给子程序,然后由子程序为其赋值。如下例所示,IN OUT参数可以为被调用的子程序传递初始值然后子程序将更新后的新值返回给调用程序: DECLARE my_sal REAL(7,2); PROCEDURE adjust_salary (emp_id INT, salary IN OUT REAL) IS .BEGIN SELECT AVG(sal) INTO my_sal FROM emp; adjust_salary(7788, my_sal); - assigns a new value to my_sal · 声明常量 声明常量跟声明变量类似,但是要加一个CONSTANT关键字,并在声明时为其赋上初始值。下例中,我们声明一个名为credit_limit的常量: credit_limit CONSTANT REAL := 5000.00; 3、游标Oracle使用工作区(work area)来执行SQL语句,并保存处理信息。PL/SQL可以让我们使用游标来为工作区命名,并访问存储的信息。游标的类型有两种:隐式和显式。PL/SQL会为所有的SQL数据操作声明一个隐式的游标,包括只返回一条记录的查询操作。对于返回多条记录的查询,我们可以显式地声明一个游标来处理每一条记录。如下例: DECLARE CURSOR c1 IS SELECT empno, ename, job FROM emp WHERE deptno = 20; 由多行查询返回的行集合称为结果集(result set)。它的大小就是满足我们查询条件的行的个数。如下图所示,显式游标"指向"当前行的记录,这可以让我们的程序每次处理一条记录。 多行查询处理有些像文件处理。例如,一个COBOL程序打开一个文件,处理记录,然后关闭文件。同样,一个PL/SQL程序打开一个游标,处理查询出来的行,然后关闭游标。就像文件指针能标记打开文件中的当前位置一样,游标能标记出结构集的当前位置。 我们可以使用OPEN,FETCH和CLOSE语句来控制游标,OPEN用于打开游标并使游标指向结果集的第一行,FETCH会检索当前行的信息并把游标指移向下一行,当最后一行也被处理完后,CLOSE就会关闭游标。 4、游标FOR循环在大多需要使用显式游标的情况下,我们都可以用一个简单的游标FOR循环来代替OPEN,FETCH和CLOSE语句。首先,游标FOR循环会隐式地声明一个代表当前行的循环索引(loop index)变量。下一步,它会打开游标,反复从结果集中取得数据并放到循环索引的各个域(field)中。当所有行都被处理过以后,它就会关闭游标。下面的例子中,游标FOR循环隐式地声明了一个emp_rec记录: DECLARE CURSOR c1 IS SELECT ename, sal, hiredate, deptno FROM emp; .BEGIN FOR emp_rec IN c1 LOOP . salary_total := salary_total + emp_rec.sal; END LOOP;END; 为了使用每一个查询到的行中的每一个字段,我们可以使用点标志(dot notation),它的作用就像一个域选择器。 5、游标变量游标变量的使用方式和游标类似,但更加灵活,因为它不绑定于某个特定的查询,所以可用于打开任何返回类型相兼容的查询语句。游标变量是真正的PL/SQL变量,我们可以为它赋值,把它传递给子程序。如下例,我们把游标变量作为存储过程open_cv的一个参数传进去,程序执行时,可以根据choice值的不同,灵活地打开不同的查询内容。 PROCEDURE open_cv(generic_cv IN OUT genericcurtyp, choice NUMBER) ISBEGIN IF choice = 1 THEN OPEN generic_cv FOR SELECT * FROM emp; ELSIF choice = 2 THEN OPEN generic_cv FOR SELECT * FROM dept; ELSIF choice = 3 THEN OPEN generic_cv FOR SELECT * FROM salgrade; END IF; .END; 6、属性PL/SQL的变量和游标都有着让我们能够直接引用它们各自的数据类型或结构的属性。数据库字段和表也有类似的属性。"%"是一个属性的指示符。 · %TYPE %TYPE可以提供一个变量或数据库字段的数据类型,这在声明存放数据库值的变量时是非常有用的。假设我们要声明一个存放表books中的字段my_title的字段的变量时,就可以这样使用%TYPE属性: my_title books.title%TYPE; 这样声明my_title有两个优点,第一,我们不必知道title具体类型;第二,如果我们改变了数据库中对该字段的数据类型定义的话,my_title的数据类型会在运行时做出相应的改变。 · %ROWTYPE 在PL/SQL中,记录用于将逻辑相关数据组织起来。一个记录是由许多相关域的组合。%ROWTYPE属性返回一个记录类型,其数据类型和数据表的数据结构相一致。这样的记录类型可以完全保存从数据表中查询(SELECT)或从游标/游标变量取出(FETCH)的行记录。 行中的字段和记录中的域对应的名称和数据类型都完全一致。下面的例子中,我们声明一个dept_rec的记录。它的域名称和数据类型与表dept中的字段名称和数据类型就完全一样。 DECLARE dept_rec dept%ROWTYPE; - declare record variable 我们可以使用"."来引用记录中的域。 my_deptno := dept_rec.deptno; 假设我们声明了一个用于检索雇员的名字、工资、雇用日期和职称的游标,我们就可以使用%ROWTYPE来声明一个类型相同的记录,如下例: DECLARE CURSOR c1 IS SELECT ename, sal, hiredate, job FROM emp; emp_rec c1%ROWTYPE; - declare record variable that represents - a row fetched from the emp table 当我们执行语句 FETCH c1 INTO emp_rec; 表emp中ename字段的值就会赋给emp_rec的ename域,sal字段值赋给sal域,依此类推。 7、控制结构流程控制是PL/SQL对SQL的最重要的扩展。PL/SQL不仅能让我们操作Oracle数据,还能让我们使用条件、循环和顺序控制语句来处理数据,如IF-THEN-ELSE,CASE,FOR-LOOP,WHILE-LOOP,EXIT-WHEN和GOTO等。 · 条件控制 我们经常需要根据环境来采取可选择的行动。IF-THEN-ELSE语句能让我们按照条件来执行一系列语句。IF用于检查条件;THEN决定在条件值为true的情况下执行;ELSE在条件值为false或null的情况才执行。 看一下下面的例子,这个程序用于处理银行事务。在我们从账户3取走$500元之前,它会先确认是否有足够的资金供我们支配。如果余额足够,程序就会更新账户(accounts)表的信息,否则的话,程序会向审核(audit)表插入一条余额不足的提示信息。 DECLARE acct_balance NUMBER(11, 2); acct CONSTANT NUMBER(4) := 3; debit_amt CONSTANT NUMBER(5, 2) := 500.00;BEGIN SELECT bal INTO acct_balance FROM accounts WHERE account_id = acct FOR UPDATE OF bal; IF acct_balance >= debit_amt THEN UPDATE accounts SET bal = bal - debit_amt WHERE account_id = acct; ELSE INSERT INTO temp VALUES (acct, acct_balance, 'Insufficient funds'); - insert account, current balance, and message END IF; COMMIT;END; 要从多个条件值中选出对应的操作时,我们可以使用CASE结构。CASE表达式会计算条件值,然后相应的操作(这个操作有可能是一个完整的PL/SQL块)。 CASE WHEN shape = 'square' THEN area := side * side; WHEN shape = 'circle' THEN BEGIN area := pi *(radius * radius); DBMS_OUTPUT.put_line('Value is not exact because pi is irrational.'); END; WHEN shape = 'rectangle' THEN area := LENGTH * width; ELSE BEGIN DBMS_OUTPUT.put_line('No formula to calculate area of a' | shape); RAISE PROGRAM_ERROR; END;END CASE; · 循环控制 LOOP语句能让我们多次执行一系列语句。LOOP循环以关键字LOOP开头,END LOOP结尾i。下面语句就是最简单的LOOP循环: LOOP - sequence of statementsEND LOOP; FOR-LOOP语句可以指定整数的范围,然后范围内每一个数字执行一次。例如,下面的循环将向数据库表插入500个数字和它们的平方根: FOR num IN 1 . 500 LOOP INSERT INTO roots VALUES (num, SQRT(num);END LOOP; WHILE-LOOP语句会按照某个条件值执行。每次循环条件值都会被计算一次,如果条件值为true,循环内的语句就会被执行,然后再次回到循环顶部,重新计算条件值。如果条件值为false或是null的话,循环就会停止,控制权被移交给下一语句。 下面的例子中,我们要找出第一个工资高于$2500的且行政级别高于雇员编号7499雇员的员工: DECLARE salary emp.sal%TYPE := 0; mgr_num emp.mgr%TYPE; last_name emp.ename%TYPE; starting_empno emp.empno%TYPE := 7499;BEGIN SELECT mgr INTO mgr_num FROM emp WHERE empno = starting_empno; WHILE salary <= 2500 LOOP SELECT sal, mgr, ename INTO salary, mgr_num, last_name FROM emp WHERE empno = mgr_num; END LOOP; INSERT INTO temp VALUES (NULL, salary, last_name); COMMIT;EXCEPTION WHEN NO_DATA_FOUND THEN INSERT INTO temp VALUES (NULL, NULL, 'Not found'); COMMIT;END; EXIT-WHEN语句可以在需要继续执行循环的情况下退出循环。当遇到EXIT语句时,WHEN子句中的条件值就会被计算。如果条件为true,循环就会结束,控制权交给下一条语句。下面的例子中,在total的值超过2500时,循环就会结束: LOOP . total := total + salary; EXIT WHEN total > 25000; - exit loop if condition is trueEND LOOP;- control resumes here · 顺序控制 GOTO语句能让我们无条件地跳转到一个标签。标签就是一个用双尖括号夹起来的未声明标示符,它必须在一个可执行语句或是PL/SQL块之前。执行时,GOTO语句将控制权交给用标签作了标记的语句或块,如下例所示: IF rating > 90 THEN GOTO calc_raise; - branch to labelEND IF;<<calc_raise>>IF job_title = 'SALESMAN' THEN - control resumes here amount := commission * 0.25;ELSE amount := salary * 0.10;END IF; 8、模块化模块化可以让我们把程序分成多个部分,这样可以把复杂的问题划分开来,更好的解决问题。PL/SQL为我们提供了块、子程序和包三个程序单元来用于模块化处理。· 子程序 子程序有两种,分别是函数和过程。子程序很像一段小型的程序,由可选的声明部分,执行控制部分和可选的异常控制部分组成。 PROCEDURE award_bonus(emp_id NUMBER) IS bonus REAL; comm_missing EXCEPTION;BEGIN - executable part starts here SELECT comm * 0.15 INTO bonus FROM emp WHERE empno = emp_id; IF bonus IS NULL THEN RAISE comm_missing; ELSE UPDATE payroll SET pay = pay + bonus WHERE empno = emp_id; END IF;EXCEPTION - exception-handling part starts here WHEN comm_missing THEN .END award_bonus; 调用时,这个过程接受一个雇员编号。它使用编号来查询雇员的commission,同时计算出15%的奖金。然后,它检查bonus总值。如果奖金为空,就会抛出异常,否则雇员的工资记录就会被更新。 · 包 PL/SQL可以让我们把逻辑相关的类型、变量、游标和子程序放在一个包内,这样更加清楚易理解。包通常有两部分组成:包说明部分和包体部分。包说明部分是应用程序的接口,它"声明"了类型、常量、变量、异常、游标和可以使用的子程序。包体用于实现包说明部分声明的子程序和游标。 下面的例子是把两个雇用相关的过程进行打包: CREATE PACKAGE emp_actions AS - package specification PROCEDURE hire_employee(empno NUMBER, ename CHAR, .); PROCEDURE fire_employee(emp_id NUMBER);END emp_actions;CREATE PACKAGE BODY emp_actions AS - package body PROCEDURE hire_employee(empno NUMBER, ename CHAR, .); IS BEGIN INSERT INTO emp VALUES (empno, ename, .); END hire_employee; PROCEDURE fire_employee(emp_id NUMBER) IS BEGIN DELETE FROM emp WHERE empno = emp_id; END fire_employee;END emp_actions; 只有在包说明中声明的内容对应用程序是可见的,而包体中的内容是不可见,无法访问的。包被编译后会存放到数据库中,当我们首次调用包中的子程序时,整个包就会被加载到内存当中,所以,后续的调用不再涉及到磁盘的读取问题。因此,包可以提高效率改善性能。 9、数据抽象数据抽象可以让我们把必要的属性提取出来,忽略那些非必须的细节问题,有助于我们更好地解决问题。一旦我们设计好一个数据结构,就可以不再考虑细节内容,而专注于操作这个数据结构的算法问题的研究。 · 集合 集合类型TABLE和VARRAY可以让我们声明索引表、嵌套表和变长数组(略称varray)。集合是类型相同的元素有序组合。在集合中,每个元素都有唯一一个能够确定该元素在集合中位置的下标索引。下面是嵌套表的一个例子: DECLARE TYPE staff IS TABLE OF employee; staffer employee; FUNCTION new_hires(hiredate DATE) RETURN staff IS BEGIN . END;BEGIN staffer := new_hires('10-NOV-98')(5); .END; 集合有些像三代语言中的数组,并且可以作为参数进行传递。 · 记录 我们知道,可以使用%ROWTYPE属性获取数据表中一行的记录类型,其实我们还可以定义自己的记录类型。 记录包含名称不可重复的域,域可以有不同的数据类型。假设我们设计了一个雇员记录类型,其中有名字、工资和雇用日期,这些项虽然类型不同,但逻辑上都是相关联的。看一下下面的例子: TYPE timerec IS RECORD( hours SMALLINT, minutes SMALLINT);TYPE meetingtyp IS RECORD( date_held DATE, DURATION timerec, - nested record LOCATION VARCHAR2(20), purpose VARCHAR2(50); 这里要注意的是,记录里可以嵌套记录类型。也就是说,记录本身也可以作为另一个记录的组成部分。 · 对象类型 PL/SQL中的面向对象编程是基于对象类型的。对象类型把数据和用于数据操作的函数和过程封装起来。其中,对象类型中的变量称为属性,函数和过程称为方法。对象类型是把大系统划分成多个逻辑实体来降低问题的复杂度,这就能使我们创建模块化、可维护和重用性好的组件了。我们在用CREATE TABLE定义对象类型的时候,常常是创建一个对真实世界对象的抽象的模板。如下面的银行账户例子中显示,模板只指定了应用程序的环境中会使用到的属性和方法: CREATE TYPE bank_account AS OBJECT( acct_number INTEGER(5), balance REAL, status VARCHAR2(10), MEMBER PROCEDURE OPEN(amount IN REAL), MEMBER PROCEDURE verify_acct(num IN INTEGER), MEMBER PROCEDURE CLOSE(num IN INTEGER, amount OUT REAL), MEMBER PROCEDURE deposit(num IN INTEGER, amount IN REAL), MEMBER PROCEDURE withdraw(num IN INTEGER, amount IN REAL), MEMBER FUNCTION curr_bal(num IN INTEGER) RETURN REAL); 运行时,当数据结构被赋值之后,我们就可以创建抽象的银行账户了。我们可以按照需求创建任意个实例(称为对象)。每个对象都有账号,余额和状态。 10、信息隐藏有了信息隐藏,我们就可以只关心给定的设计级别的算法和数据结构设计。信息隐藏能把高阶的设计决定从频繁改变的低阶设计细节分离出来。 · 算法 我们可以通过自顶而下(top-down)的设计来实现算法隐藏。一旦我们明确了低阶过程的实现目的并定义好相应的接口说明,就可以忽略实现细节部分。例如,我们只需要知道将一个雇员的工资金额传递给过程raise_salary就可以提高该雇员的工资。任何对raise_salary方法的变动,对于应用程序来说,都是透明的。 · 数据结构 我们可以通过数据封装来实现信息隐藏。开发一组操作数据结构的工具子程序,就可以让使用它的用户和开发人员分离。这样一来,开发人员只需了解如何使用这些子程序来操作数据,并不需要知道数据真正的含义。 使用PL/SQL包,我们就可以指定哪些子程序是公有哪些是私有,更好的提供封装,简化维护。 11、错误控制PL/SQL能够轻松的发现并处理预定义和用户定义的错误条件(即异常)。错误发生时,异常就会被抛出。也就是说,正常的执行会终止,程序控制权将交给PL/SQL块或子程序的异常处理部分。为控制被抛出的异常,我们需要单独编写异常控制句柄(即异常控制程序)。 预定义异常会被系统隐式地抛出,例如,用一个数字除以零,PL/SQL就会自动抛出预定义异常ZERO_DIVIDE。对于用户自定义异常,必须由我们显式地使用RAISE语句抛出。 我们可以在任何PL/SQL块或子程序的声明部分定义自己的异常。在执行部分,我们检查那些需要特别对待的条件,如果错误条件满足,就可以使用RAISE抛出异常。在下面的例子中,我们要计算售货员的奖金。奖金的多少取决于他的工资(salary)和佣金(commission)。所以,如果佣金为空的话,我们就要抛出异常comm_missing。 DECLARE . comm_missing EXCEPTION; - declare exceptionBEGIN . IF commission IS NULL THEN RAISE comm_missing; - raise exception END IF; bonus := (salary * 0.10) +(commission * 0.15);EXCEPTION WHEN comm_missing THEN - process the exception .END; 二、PL/SQL架构PL/SQL的编码和运行时系统是一项技术,而不是一个独立的产品。可以把这项技术想象成一个能够编译并运行PL/SQL块和子程序的引擎。这个引擎可以安装在Oracle服务器上或安装在Oracle Forms,Oracle Reports这样的开发工具中。所以,PL/SQL可以在两种环境中存在: 1. Oracle数据库服务器 2. Oracle开发工具 这两种环境是独立的。PL/SQL虽被绑定到Oracle服务器上,但在某些工具中是无法使用的。在这两种环境下,PL/SQL引擎都能接受有效的PL/SQL块或子程序。下图是PL/SQL引擎处理匿名块的过程,引擎会处理过程化语句,而把SQL语句发送给Oracle服务器端的SQL语句执行程序(SQL Statement Executor)来处理。 1、Oracle数据库中缺乏本地PL/SQL引擎的应用开发工具就必须依赖于Oracle来处理PL/SQL块和子程序。Oracle服务器除了能够处理SQL语句外,还会处理PL/SQL块和子程序,它会将块与子程序传给它本地的PL/SQL引擎。 · 匿名块 匿名PL/SQL块能被嵌到Oracle预编译程序(Oracle Precompiler)或是OCI程序中。运行的时候,不含PL/SQL引擎的程序会把这些块发送到Oracle服务器编译并执行。 · 存储过程 子程序可以独立编译并存储在Oracle数据库。使用CREATE语句显式创建的子程序就是一个"存储"子程序。一旦编译并保存到数据词典中,它就成了一个模式对象(schema object),可以被许多连到数据库的应用程序调用。 定义在包内的存储子程序称为打包子程序(packaged subprogram);单独定义的存储子程序称为独立子程序(standalone subprogram);而在另外一个子程序或PL/SQL块内定义的存储子程序称为本地子程序,这样的子程序不能被其他应用程序调用,只供本地使用。 存储过程执行效率高,耗内存少,应用集成,安全性好。例如,我们设计出一套存储过程和函数时,应用程序就可以调用这些函数和方法,这样就能避免大量的冗余代码提高效能。 我们可以从数据库触发器、其他存储子程序、Oracle预编译程序、OCI程序或是SQL*Plus等,调用存储子程序。例如,我们可以像下面这样从SQL*Plus中调用独立子程序create_dept: SQL> CALL create_dept('FINANCE', 'NEW YORK'); 子程序以分析、编译过的形式保存在数据库中。所以,在被调用时,它们会被立即加载并传递到PL/SQL引擎。并且,它们还会利用共享内存,这样,每次只要有一个子程序需要被加载到内存,就能被多个用户调用执行。 · 数据库触发器 数据库触发器是与数据库中某个数据表、视图或事件相关联的存储子程序。举一个例子,我们可以让Oracle数据库在INSERT、UPDATE或DELETE表达式影响一个表之前或之后来自动激活一个触发器。触发器的用途之一就是审核数据修改。例如,下面的表级(table-level)触发器会在emp表的salaries字段更新后被激活。 CREATE TRIGGER audit_sal AFTER UPDATE OF sal ON emp FOR EACH ROWBEGIN INSERT INTO emp_audit VALUES .END; 触发器执行部分可以包括过程语句和SQL数据操作语句。除了表级触发器以外,还包含替代触发器(instead-of triggers for views)和系统触发器(system-event trigger)。 2、Oracle工具中在包含PL/SQL引擎的条件下,应用开发工具就能够处理PL/SQL块和子程序。开发工具会把块传给它的本地PL/SQL引擎。引擎会在应用程序段执行所有的过程语句,只把SQL语句发送给Oracle。因此,大多部分工作会在应用程序端完成,而不是在服务器端。进一步说,如果块中不包含任何SQL语句的话,这个引擎会在应用程序端执行全部的代码。 三、PL/SQL的优势PL/SQL是一个可移植、高效的事务处理语言: 1. 支持SQL 2. 支持面向对象编程 3. 良好的性能 4. 效率高 5. 可移植 6. 与Oracle集成 7. 高度安全 1、SQL的支持SQL因为它的灵活、强大和易学,已经成为标准的数据库语言。只要几个像SELECT、INSERT、UPDATE和DELETE这样简单的命令就能轻松地操作关系数据库中的数据。SQL是非过程化的,这就是说我们可以决定做我们想做的,但不能决定如何做。Oracle会决定处理我们请求的最佳方案。 PL/SQL能让我们使用所有的SQL数据操作,游标控制和事务控制命令;也可以使用所有的SQL函数,操作符和伪列。所以,我们可以灵活安全地操作Oracle数据。PL/SQL完全支持SQL数据类型,这就减少了我们的应用程序和数据库间数据传递时的类型转换。 PL/SQL也支持动态SQL语句,这样能够让我们的应用程序更加灵活通用。程序可以在运行时处理SQL数据定义、数据控制和会话控制语句。 2、面向对象的支持对象类型是理想的面向对象建模工具,它能帮助我们创建复杂的应用程序。除了能创建模块化,易维护和重用性高的软件组件外,对象类型还可以让不同开发组的程序员并发地开发组件。 对象类型通过对数据操作的封装,把数据维护代码从SQL脚本和PL/SQL块中提取出来,放到独立的方法中去。同样,对象类型也可以隐藏实现,这样我们就在不影响客户端程序的情况下改变实现细节。 此外,真实世界的复杂实体和关系也能够映射到对象类型中去。这样我们的程序就能更好将模拟的内容反映出来。 3、良好的性能如果没有PL/SQL的话,Or