- 浏览: 1764830 次
- 性别:
- 来自: 成都
文章分类
- 全部博客 (520)
- Oracle (10)
- Oracle错误集 (8)
- Oracle安装升级 (15)
- Oracle日常管理 (51)
- Oracle字符集 (7)
- Oracle备份恢复 (24)
- Oracle优化 (4)
- Oracle编程 (52)
- Oracle导入导出 (19)
- Oracle体系结构 (15)
- Oracle网络 (2)
- Oracle安全 (2)
- Oracle权限 (3)
- Oracle数据字典和性能视图 (2)
- Oracle常用地址 (5)
- SQLPLUS专栏 (7)
- SqlServer (13)
- SqlServer2005编程 (27)
- SqlServer2005管理 (15)
- MySQL (20)
- Dorado应用 (1)
- C# (24)
- Arcgis Server开发 (20)
- ArcSDE技术 (19)
- UML学习 (2)
- 设计模式 (2)
- JAVA EE (4)
- JavaScript (3)
- OFBIZ (27)
- JAVA WEB开发 (22)
- Linux&Unix (34)
- SHELL编程 (14)
- C语言 (11)
- 网络协议 (14)
- FREEMARKER (2)
- GROOVY (2)
- JAVA语言 (3)
- 防火墙 (0)
- PHP (2)
- Apache (2)
- Loader Runner (1)
- Nginx (3)
- 数据库理论 (2)
- maven (1)
最新评论
-
怼怼怼怼:
oracle的timestamp类型使用 -
怼怼怼怼:
oracle的timestamp类型使用 -
怼怼怼怼:
oracle的timestamp类型使用 -
pg_guo:
感谢
oracle中查看用户权限 -
xu234234:
5、MapResourceManager控件中添加了两个服务, ...
北京ArcGis Server应用基础培训笔记1
一、什么是子程序
子程序就是能够接受参数并被其他程序所调用的命名PL/SQL块。PL/SQL子程序有两种类型,过程和函数。一般地,过程用于执行一个操作,而函数用于计算一个结果值。
与未命名或匿名PL/SQL块一样,子程序也有声明部分,执行部分和一个可选的异常处理部分。声明部分包含类型、游标、常量、变量、异常和嵌套子程序的声明。这些内容都是本地的,在程序退出时会自动销毁。执行部分包含赋值语句、流程控制语句和Oracle的数据操作语句。异常处理部分包含异常处理程序。思考下面用于记入借方银行账户的debit_account过程:
PROCEDURE debit_account(acct_id INTEGER, amount REAL) IS
old_balance REAL;
new_balance REAL;
overdrawn EXCEPTION;
BEGIN
SELECT bal
INTO old_balance
FROM accts
WHERE acct_no = acct_id;
new_balance := old_balance - amount;
IF new_balance < 0 THEN
RAISE overdrawn;
ELSE
UPDATE accts
SET bal = new_balance
WHERE acct_no = acct_id;
END IF;
EXCEPTION
WHEN overdrawn THEN
...
END debit_account;
在被调用时,这个过程接受一个银行账号和借贷金额。它使用账号从accts表中查询账目结算信息。然后用借款金额计算新的账目结算。如果计算后的余额比零小,异常就会被抛出;否则,该账号相关信息就会被更新。
二、子程序的优点
子程序能提供扩展性,它能够让我们根据自己的需求来编写特定的PL/SQL。比如,我们需要一个能够创建新部门的过程,就可以像下面这样编写代码:
PROCEDURE create_dept(new_dname VARCHAR2, new_loc VARCHAR2) IS
BEGIN
INSERT INTO dept
VALUES (deptno_seq.NEXTVAL, new_dname, new_loc);
END create_dept;
子程序还能提供模块化,就是说它可以把一个程序定义成多个模块,更易管理。这样,我们就可以用自顶而下的设计(top-down design)和逐步求精(stepwise refinement)的方法来解决问题。
此外,子程序在提高程序的重用性和可维护方面也是很有用的。只要编译成功,子程序就可以放心地用在很多应用程序中。如果它的定义内容发生了改变,受到影响的只有子程序本身而已,这就简化了维护过程。最后,子程序还有助于逻辑的抽象。使用子程序时,我们需要知道的是它们的功能,而不是它们实现功能的细节问题。
三、理解PL/SQL过程
过程是一个能执行某个特定操作的子程序。我们可以用下面的语法来编写过程:
[CREATE [OR REPLACE]]
PROCEDURE procedure_name[(parameter[, parameter]...)]
[AUTHID {DEFINER | CURRENT_USER}] {IS | AS}
[PRAGMA AUTONOMOUS_TRANSACTION;]
[local declarations]
BEGIN
executable statements
[EXCEPTION
exception handlers]
END [name];
parameter的含义如下:
parameter_name [IN | OUT [NOCOPY] | IN OUT [NOCOPY]] datatype
[{:= | DEFAULT} expression]
CREATE子句能让我们创建保存在数据库中的独立过程。我们可以从SQL*Plus中或是在使用动态SQL的程序中执行CREATE PROCEDURE语句。
AUTHID子句决定了存储过程是按所有者权限(默认)调用还是按当前用户权限执行,也能决定在没有限定修饰词的情况下,对所引用的对象是按所有者模式进行解析还是按当前用户模式进行解析。我们可以指定CURRENT_USER来覆盖掉程序的默认行为。
编译指示AUTONOMOUS_TRANSACTION会告诉PL/SQL编译器把过程标记为自治(独立)。自治事务能让我们把主事务挂起,执行SQL操作,提交或回滚自治事务,然后再恢复主事务。
我们不能对参数的数据类型进行约束,如下例中对acct_id的声明就是不合法的,因为它对CHAR类型进行了长度限制:
PROCEDURE reconcile (acct_id CHAR(5)) IS ... -- illegal
但是,我们可以使用下面的方法间接的对字符的长度进行限制:
DECLARE
SUBTYPE Char5 IS CHAR(5);
PROCEDURE reconcile (acct_id Char5) IS ...
过程有两个部分,过程说明和过程体。说明部分由关键字PROCEDURE开头,以过程名或参数列表结尾。参数声明是可选的。没有参数的过程是不用使用圆括号的。
过程体由关键字IS(或AS)开头,并以END结尾,END后面可以跟上一个可选的过程名。过程体有三个部分:声明、执行和可选的异常处理。
声明部分包括本地声明,它处于IS和BEGIN之间。在匿名PL/SQL块使用的关键字DECLARE在这里不再需要。执行部分包括许多语句,它们被放到BEGIN和EXCEPTION(或END)之间,并且至少要有一条语句出现在过程的执行部分。NULL语句可以满足这个需求。异常处理部分包含异常处理程序,它被放在关键字EXCEPTION和END之间。
在下面的过程raise_salary中,我们会根据给定的金额来为雇员加薪:
PROCEDURE raise_salary(emp_id INTEGER, amount REAL) IS
current_salary REAL;
salary_missing EXCEPTION;
BEGIN
SELECT sal
INTO current_salary
FROM emp
WHERE empno = emp_id;
IF current_salary IS NULL THEN
RAISE salary_missing;
ELSE
UPDATE emp
SET sal = sal + amount
WHERE empno = emp_id;
END IF;
EXCEPTION
WHEN NO_DATA_FOUND THEN
INSERT INTO emp_audit
VALUES (emp_id, 'No such number');
WHEN salary_missing THEN
INSERT INTO emp_audit
VALUES (emp_id, 'Salary is null');
END raise_salary;
在调用时,过程接受雇员编号和薪资调整金额,然后用雇员编号从emp表找出指定雇员的当前工资。如果雇员编号无法找到或是当前工资为空,异常就会被抛出,否则工资就会被更新。
过程可以作为一个PL/SQL语句来调用。例如,我们可以像下面这样调用raise_salary:
raise_salary(emp_id, amount);
四、理解PL/SQL函数
函数是一个能够计算结果值的子程序,函数除了有一个RETURN子句之外,其它结构跟过程类似。我们可以用下面的语法来编写(本地)函数:
[CREATE [OR REPLACE ] ]
FUNCTION function_name [ ( parameter [ , parameter ]... ) ] RETURN datatype
[ AUTHID { DEFINER | CURRENT_USER } ]
[ PARALLEL_ENABLE
[ { [CLUSTER parameter BY (column_name [, column_name ]... ) ] |
[ORDER parameter BY (column_name [ , column_name ]... ) ] } ]
[ ( PARTITION parameter BY
{ [ {RANGE | HASH } (column_name [, column_name]...)] | ANY }
) ]
]
[DETERMINISTIC] [ PIPELINED [ USING implementation_type ] ]
[ AGGREGATE [UPDATE VALUE] [WITH EXTERNAL CONTEXT]
USING implementation_type ] {IS | AS}
[ PRAGMA AUTONOMOUS_TRANSACTION; ]
[ local declarations ]
BEGIN
executable statements
[ EXCEPTION
exception handlers ]
END [ name ];
函数的语法结构与过程类似,这里就不再重复。但有几个不同点还是需要注意的。
PARALLEL_ENABLE选项能声明一个在并发DML操作的从属会话(slave session)中被安全调用的存储函数。主(logon)会话的状态不会被从属会话所共享。每个从属会话都有它自己的状态,这是在会话开始时初始化的。函数的结果不应依赖于会话(静态)变量的状态。否则结果就可能随着会话而发生变化。
提示DETERMINISTIC能帮助优化程序避免冗余的函数调用。如果存储函数的调用跟前一次调用时所使用的参数相同,优化程序就直接选出前一次的计算结果值。函数结果不应该依赖于会话变量或模式对象的状态。否则结果会随着调用而发生变化。只有DETERMINISTIC函数才允许被函数索引或是参数query_rewrite_enabled为TRUE的实体化视图调用。
我们不能对参数或是函数返回值的类型添加约束,但可以像前面的过程那样使用间接的约束方法。
思考下面的函数sal_ok,它的作用是检查工资是否超出限定范围:
FUNCTION sal_ok(salary REAL, title VARCHAR2)
RETURN BOOLEAN IS
min_sal REAL;
max_sal REAL;
BEGIN
SELECT losal, hisal
INTO min_sal, max_sal
FROM sals
WHERE job = title;
RETURN (salary >= min_sal) AND(salary <= max_sal);
END sal_ok;
调用时,函数接受雇员的工资金额和职别,然后使用职别从数据表sals选择工资范围。函数标识符sal_ok由RETURN语句返回的布尔值赋值。如果salary超过限定范围,sal_ok值就是FALSE,否则就是TRUE。
函数可以作为表达式的一部分而被调用,如下例所示。函数标识符sal_ok就像一个变量一样,它的值是由传递进去的参数所决定的。
IF sal_ok(new_sal, new_title) THEN ...
1、使用RETURN语句
RETURN语句能够立即结束当前执行的子程序并把控制权交还给调用者。然后程序继续执行子程序调用之后的语句。(不要把RETURN语句和函数声明中的RETURN子句搞混淆了,声明中的RETURN只是用于指明函数返回值的数据类型。)
子程序能包含几个RETURN语句。最后一个语句不一定非得是RETURN语句。只要执行RETURN语句就能立即结束当前子程序。但是,在一个子程序包含有多个出口点不是一个好习惯。
在过程中,RETURN语句不能返回值,也不能返回任何表达式。它的作用只是在过程到达正常的过程结尾之前将控制权交给调用者。
但是,在函数中,RETURN语句必须包含一个表达式,该表达式的值会在RETURN语句执行时被计算。计算的结果赋给函数标识符,标识符的作用相当于RETURN说明中的类型的变量。观察下面返回指定银行账户的余额的函数balance:
FUNCTION balance(acct_id INTEGER)
RETURN REAL IS
acct_bal REAL;
BEGIN
SELECT bal
INTO acct_bal
FROM accts
WHERE acct_no = acct_id;
RETURN acct_bal;
END balance;
下例演示了如何在RETURN语句中使用复杂的表达式:
FUNCTION compound(years NUMBER, amount NUMBER, rate NUMBER)
RETURN NUMBER IS
BEGIN
RETURN amount * POWER((rate / 100) + 1, years);
END compound;
函数中,至少要有一条执行路径能够到达RETURN语句。否则,在运行时就会得到函数没有返回值的错误。
2、控制PL/SQL子程序的副影响
为了能在SQL中被调用,存储函数必须遵守以下"纯度"规则,这些规则能控制函数副作用:
- 当从SELECT语句或是并发的INSERT、UPDATE、DELETE语句中调用函数时,它不能修改任何数据表。
- 当从INSERT、UPDATE或DELETE语句中调用函数时,它不能查询或修改这些语句所能影响到的数据表。
- 当从SELECT、INSERT、UPDATE或DELETE语句中调用函数时,它不能执行SQL事务控制语句(如COMMIT),会话控制语句(如SET ROLE)或系统控制语句(如ALTER SYSTEM)。同样,它也不能执行数据定义语句(如CREATE),因为这些语句都是自动提交事务的。
如果函数内部任何一条SQL语句与上述规则相冲突,我们就会在运行时得到错误(语句被分析的时候)。
为了检查这些冲突项,我们可以使用编译指示RESTRICT_REFERENCES(编译器指令)。编译指示能判断函数是否读写数据表或包中的变量。例如在下面的函数中,编译指示就能判断出函数credit_ok不写数据库(WNDS)也不读取包(RNPS):
CREATE PACKAGE loans AS
...
FUNCTION credit_ok RETURN BOOLEAN;
PRAGMA RESTRICT_REFERENCES (credit_ok, WNDS, RNPS);
END loans;
注意:一个静态的INSERT、UPDATE或DELETE语句总是与WNDS相冲突的;如果读取了数据库字段,它也会与RNDS冲突。一个动态INSERT、UPDATE或DELETE语句总与WNDS和RNDS冲突。
五、声明PL/SQL子程序
我们可以在PL/SQL块、子程序或包中声明子程序。但是,子程序只能在其他内容声明之后再声明。
PL/SQL需要我们先声明标识然后才能引用它们。所以,在使用子程序之前必须要先声明。例如,下面对过程award_bonus的声明就是非法的,因为它在过程calc_rating未声明之前就开始调用它:
DECLARE
...
PROCEDURE award_bonus IS
BEGIN
calc_rating(...); -- undeclared identifier
...
END;
PROCEDURE calc_rating (...) IS
BEGIN
...
END;
这种情况下,我们只要简单地把过程calc_rating放到award_bonus之前就可以了。但是简单的做法不一定总是有效的,如:两个过程相互引用或是我们就想按逻辑或字母顺序来定义他们。
我们可以使用"向前声明"来解决这个问题,向前声明由子程序说明和一个分号组成。在下面的例子中,向前声明通知PL/SQL,过程calc_rating的体部分可以在块的后面找到。
DECLARE
PROCEDURE calc_rating ( ... ); -- forward declaration
...
虽然形式参数列表在向前声明中已经出现过了,但它也必须出现在子程序体中。子程序体可以放在向前声明之后的任何地方,但它们必须出现在同一个程序单元中。
六、子程序打包
我们可以把逻辑相关的子程序打包后放到数据库中。那样,子程序就能被许多应用程序共享。子程序说明部分放在包说明部分;子程序体放在包体,子程序体对应用程序是不可见的。因此,包能帮助我们隐藏程序的实现细节。如下例:
CREATE PACKAGE emp_actions AS -- package spec
PROCEDURE hire_employee(emp_id INTEGER, NAME VARCHAR2, ...);
PROCEDURE fire_employee(emp_id INTEGER);
PROCEDURE raise_salary(emp_id INTEGER, amount REAL);
...
END emp_actions;
CREATE PACKAGE BODY emp_actions AS -- package body
PROCEDURE hire_employee(emp_id INTEGER, NAME VARCHAR2, ...) IS
BEGIN
...
INSERT INTO emp
VALUES (emp_id, NAME, ...);
END hire_employee;
PROCEDURE fire_employee(emp_id INTEGER) IS
BEGIN
DELETE FROM emp
WHERE empno = emp_id;
END fire_employee;
PROCEDURE raise_salary(emp_id INTEGER, amount REAL) IS
BEGIN
UPDATE emp
SET sal = sal + amount
WHERE empno = emp_id;
END raise_salary;
...
END emp_actions;
我们还能够直接在包体内定义子程序而不用在包说明部分编写它们的说明。但是,这样的子程序只能在包内的使用。
七、形参VS实参
子程序使用参数来传递信息。调用时子程序参数列表中引用的变量或表达式是实际参数(actual parameter,以下简称实参)。例如,下面的过程列出了两个实参emp_num和amout:
raise_salary(emp_num, amount);
下面的过程调用演示了用表达式作为实参:
raise_salary(emp_num, merit + cola);
子程序声明和子程序体中引用的变量是形式参数(formal parameter,以下简称形参)。例如,下面的过程声明了两个形参emp_id和amount:
PROCEDURE raise_salary(emp_id INTEGER, amount REAL) IS
BEGIN
UPDATE emp
SET sal = sal + amount
WHERE empno = emp_id;
END raise_salary;
好的编程习惯就是使用不同命名的形参和实参。
调用过程raise_salary时,实参的内容会被赋值到对应的形参上,如果有必要的话,在赋值之前PL/SQL会帮助我们进行类型转换。例如,下面对raise_salary的调用就是有效的:
raise_salary(emp_num, '2500');
实参和它对应的形参必须类型兼容,例如,PL/SQL是不能把数据从DATE类型转到REAL类型的。下面的过程调用就会引起预定义异常VALUE_ERROR,因为PL/SQL不能把第二个实参转成一个数字:
raise_salary(emp_num, '$2500'); -- note the dollar sign
八、位置标示法VS名字标示法
在调用子程序时,我们既可以使用位置标示法又可以使用名字标示法来编写实参。也就是说,我们可以按位置或名称来把实参和形参关联起来。如下例所示:
DECLARE
acct INTEGER;
amt REAL;
PROCEDURE credit_acct(acct_no INTEGER, amount REAL) IS ...
我们可以使用四种等价的方法来调用过程credit_acct:
BEGIN
credit_acct(acct, amt); -- positional notation
credit_acct(amount => amt, acct_no => acct); -- named notation
credit_acct(acct_no => acct, amount => amt); -- named notation
credit_acct(acct, amount => amt); -- mixed notation
1、使用位置标示法
第一个过程调用使用了位置标示法。PL/SQL编译器将第一个实参acct和第一个形参acct_no关联,并把第二个实参amt和第二个形参amount关联。
2、使用名字标示法
第二个过程调用使用了名字标示法。箭头(=>)作为关联操作符,把左边的实参和右边的形参关联起来。
第三个过程调用也使用了名字标示法,而且我们可以随意安排参数的位置。所以,我们不需要知道形参的在参数列表中的顺序。
3、使用混合标示法
第四个过程调用使用了名字标示法和位置标示法。在这种情况下,位置标示法必须在名字标示法之前,不能反过来使用,像下面这样的调用方法就是不合法的:
credit_acct(acct_no => acct, amt); -- illegal
九、指定子程序参数模式
我们可以使用参数的模式来定义形式参数的行为。一共有三种模式:IN、OUT和IN OUT。但是,最好避免在函数中使用OUT和IN OUT模式。函数的作用是用来接受零个或多个参数然后返回一个值。用函数返回多个值不是个好习惯。同样,函数应该避免产生负影响,那样会改变那些对子程序来说是非本地的变量值。
1、使用IN模式
IN模式能让我们把值传递给被调用子程序,在子程序中,IN模式参数的作用就像常量一样。因此,它不能被赋值。例如,下面的赋值语句就会引起编译错误:
PROCEDURE debit_account(acct_id IN INTEGER, amount IN REAL) IS
minimum_purchase CONSTANT REAL DEFAULT 10.0;
service_charge CONSTANT REAL DEFAULT 0.50;
BEGIN
IF amount < minimum_purchase THEN
amount := amount + service_charge; -- causes compilation error
END IF;
...
END debit_account;
IN模式形参对应的实参可以是常量、文字、被初始化的变量或是表达式。与OUT和IN OUT模式的参数不同,我们可以为IN模式的参数初始化一个默认值。
2、使用OUT模式
OUT模式的参数能让我们把值返回给子程序的调用者。在子程序中,OUT模式参数的作用就像变量。这也就意味着我们可以把它当作本地变量来使用,例如:
PROCEDURE calc_bonus(emp_id IN INTEGER, bonus OUT REAL) IS
hire_date DATE;
bonus_missing EXCEPTION;
BEGIN
SELECT sal * 0.10, hiredate
INTO bonus, hire_date
FROM emp
WHERE empno = emp_id;
IF bonus IS NULL THEN
RAISE bonus_missing;
END IF;
IF MONTHS_BETWEEN(SYSDATE, hire_date) > 60 THEN
bonus := bonus + 500;
END IF;
...
EXCEPTION
WHEN bonus_missing THEN
...
END calc_bonus;
与OUT模式的形参对应的实参必须是变量;它不能是常量或表达式。例如,下面的调用就不合法:
calc_bonus(7499, salary + commission); -- causes compilation error
一个OUT实参在子程序调用之前是可以有值的。但是,在子程序调用时,这个值就会丢失,除非我们使用了NOCOPY编译器提示或是子程序因未捕获异常而终止。
与变量一样,OUT模式的形参会被初始化为NULL.所以,一个OUT模式的形参的数据类型是不能有NOT NULL约束的(包括内置类型NATURALN和POSITIVEN)。否则的话,PL/SQL就会抛出VALUE_ERROR异常,见下例:
DECLARE
SUBTYPE counter IS INTEGER NOT NULL;
ROWS counter := 0;
PROCEDURE count_emps(n OUT counter) IS
BEGIN
SELECT COUNT(*)
INTO n
FROM emp;
END;
BEGIN
count_emps(ROWS); -- raises VALUE_ERROR
在子程序退出之前,它必须要显式地为所有的OUT模式形参赋值。否则对应的实参值就为空。如果成功地退出子程序,PL/SQL就会把值赋给实参。但是,如果有未捕获异常发生,PL/SQL就不会为实参赋值。
3、使用IN OUT模式
一个IN OUT模式的参数能让我们把它的初始值传递给被调用的子程序,然后再把子程序更新后的值传递给调用者。在子程序中,一个IN OUT模式参数的作用就像一个初始化了的变量。因此,它能够被赋值,而且它的值还可以赋给其他的变量。
与IN OUT模式形参对应的实参必须是变量;它不可以是常量或表达式。如果成功地退出子程序,PL/SQL就会为实参赋值。但是,如果有未捕获异常发生,PL/SQL就不会为实参赋值。
4、子程序参数模式总结
下表总结了我们应该知道关于参数模式的所有内容:
默认 | 必须被指定 | 必须被指定 |
向子程序传值 | 向调用者返回值 | 向子程序传递初始值并向调用者返回 更新后的结果值 |
形参的作用同常量相同 | 形参的作用同变量相同 | 形参的作用同被初始化过的变量相同 |
形参不能被赋值 | 形参必须被赋值 | 形参应该被赋值 |
实参可以是常量、被初始化的变量、 文字或表达式 |
形参必须是变量 | 形参必须是变量 |
形参按引用传递 | 形参按值传递,除非使用了NOCOPY | 形参按值传递,除非使用了NOCOPY |
十、使用NOCOPY编译提示传递大型数据结构
假定子程序声明了一个IN模式参数、一个OUT模式参数和一个IN OUT模式参数。在调用子程序时,IN模式的是按引用传递的,即把指向IN模式的实参指针赋给形参。所以,两个参数引用都指向同一块内存地址,这块内存存放了实参的值。
默认情况下,OUT和IN OUT模式的参数都是按值传递的。就是把实参的值拷贝到对应的形参上。然后,如果子程序正常结束,被赋到OUT和IN OUT形参上的值就会拷贝到对应的实参上。
当参数是大型数据结构时,如集合、记录和对象实例,把它们的内容全部拷贝给形参会降低执行速度,消耗大量内存。为了防止这样的情况发生,我们可以使用NOCOPY提示来让编译器按引用传递OUT和IN OUT模式的参数。在下面的例子中,我们请求编译器按引用的方式来传递IN OUT参数my_staff:
DECLARE
TYPE Staff IS VARRAY(200) OF Employee;
PROCEDURE reorganize (my_staff IN OUT NOCOPY Staff) IS ...
记住,NOCOPY只是一个提示,而不是指令。所以,编译器也许仍旧会把my_staff按值传递,即使我们已经发出请求了。但是,通常情况下 NOCOPY是可以成功的。下例中,我们把一个含有25000条记录的本地嵌套表中分别传递给两个没有任何功能的过程。没有使用NOCOPY的记录花费 21秒,而使用的花费不到1秒:
SQL> SET SERVEROUTPUT ON
SQL> GET test.sql
1 DECLARE
2 TYPE EmpTabTyp IS TABLE OF emp%ROWTYPE;
3 emp_tab EmpTabTyp := EmpTabTyp(NULL); -- initialize
4 t1 NUMBER(5);
5 t2 NUMBER(5);
6 t3 NUMBER(5);
7 PROCEDURE get_time (t OUT NUMBER) IS
8 BEGIN SELECT TO_CHAR(SYSDATE,'SSSSS') INTO t FROM dual; END;
9 PROCEDURE do_nothing1 (tab IN OUT EmpTabTyp) IS
10 BEGIN NULL; END;
11 PROCEDURE do_nothing2 (tab IN OUT NOCOPY EmpTabTyp) IS
12 BEGIN NULL; END;
13 BEGIN
14 SELECT * INTO emp_tab(1) FROM emp WHERE empno = 7788;
15 emp_tab.EXTEND(24999, 1); -- copy element 1 into 2..25000
16 get_time(t1);
17 do_nothing1(emp_tab); -- pass IN OUT parameter
18 get_time(t2);
19 do_nothing2(emp_tab); -- pass IN OUT NOCOPY parameter
20 get_time(t3);
21 dbms_output.put_line('Call Duration (secs)');
22 dbms_output.put_line('--------------------');
23 dbms_output.put_line('Just IN OUT: ' || TO_CHAR(t2 - t1));
24 dbms_output.put_line('With NOCOPY: ' || TO_CHAR(t3 - t2));
25* END;
SQL> /
Call Duration (secs)
--------------------
Just IN OUT: 21
With NOCOPY: 0
1、权衡NOCOPY所带来的良好性能
NOCOPY能为我们带来良好的性能,但它也能带来以下几个方面的影响:
- 因为NOCOPY只是一个提示,不是指令,所以编译器可以把NOCOPY参数按值或按引用的方式传递给子程序。所以,如果子程序因发生未捕获异常而退出时,我们就不能再信赖实参中的值了。
- 默认地,如果子程序异常退出,赋给OUT和IN OUT参数的值就不会拷贝到对应的实参上,这看起来有点像回滚操作。但是,对于按引用传递的NOCOPY参数来说,我们对形参所作的更改会立即在对应的实参上体现出来。所以,即使子程序是因异常发生而结束,它所做的变更内容也不会"回滚"。
- 目前,RPC协议允许我们只按值传递参数。例如,如果我们把一个含有NOCOPY参数的本地过程传到远程站点,这些参数就不再按引用传递了。
还有,使用NOCOPY会增加参数别名出现的可能性。
2、NOCOPY的限制
在以下几种情况中,PL/SQL编译器会忽略NOCOPY提示而直接使用按值传递参数的方法(不发生错误的情况下):
- 实参是索引表中的一个元素。这个限制并不适用于整个索引表。
- 实参是受约束的(如精度或NOT NULL等)。这个约束不会扩展到元素或属性。同样,对长度受限的字符串也不适用。
- 实参和形参都是记录,其中一个或两个使用了%ROWTYPE或%TYPE声明,且在记录中对应域的约束不同。
- 实参和形参都是记录,实参是作为游标FOR循环的索引而被声明的(隐式声明),记录之间对应域的约束不同。
- 实参传递需要进行隐式地数据类型转换。
- 子程序被外部或远程过程调用。
十一、使用子程序参数的默认值
如下例所示,我们可以为IN模式的参数初始化默认值。这样,我们就可以把不同个数的参数传给子程序,其中既可以使用参数默认值又可以使用我们传入的参数值覆盖掉默认值。并且,我们还可以在不修改每个子程序调用的情况下添加新的参数。
PROCEDURE create_dept(
new_dname VARCHAR2 DEFAULT ’temp’,
new_loc VARCHAR2 DEFAULT ’temp’
) IS
BEGIN
INSERT INTO dept
VALUES (deptno_seq.NEXTVAL, new_dname, new_loc);
...
END;
如果实参没有被传入,它所对应的形参就会使用定义时的默认值。下面是对过程create_dept的调用:
create_dept;
create_dept('MARKETING');
create_dept('MARKETING', 'NEW YORK');
第一个调用没有传入任何实参,所以子程序会使用两个默认的参数值;而第二个调用只为第一个参数指定了实参,这样,子程序会使用第一个参数传入的值和第二个参数的默认值;最后一个调用接受两个实参,它们将对应的形参默认值覆盖,子程序就使用两个传入的值。通常我们使用位置标示法覆盖形参的默认值,但是,我们不能靠省略实参来跳过它们对应的形参。例如,像下面这样把实参值"NEW YORK"和形参new_dname关联的做法是不对的:
create_dept('NEW YORK'); -- incorrect
create_dept(, 'NEW YORK'); -- not allowed
我们也不可以靠放置一个占位符来解决这个问题。例如,下面的调用就是不允许的:
create_dept(, 'NEW YORK'); -- not allowed
当出现这种情况,我们就需要使用名字标示法:
create_dept(new_loc => 'NEW Y
发表评论
-
To_Date函数用法
2014-11-07 11:04 1723spl> select * from emp ... -
Oracle中的MD5加密
2014-02-15 10:18 1468一、技术点 1、 DBMS_OBFUSCATION_TO ... -
oracle行列转换总结
2014-02-15 10:18 1009最近论坛很多人提的问题都与行列转换有关系,所以我对行列转换 ... -
Oracle数据库中的''与NULL的关系
2014-02-15 10:17 1323在Oracle数据库中''与NULL是等价的。均表示空值, ... -
快速删除重复的记录
2014-02-15 10:17 648做项目的时候,一位同事导数据的时候,不小心把一个表中的数据 ... -
Oracle的rownum原理和使用
2014-02-15 10:17 1114Oracle的rownum原理和使用 在Oracle中, ... -
ORACLE中查询某个字段包含回车换行符
2013-04-11 15:42 11294很简单,但是很多人一下估计还不一定知道。 select * ... -
ORACLE触发器(转)
2013-03-06 23:58 1162本篇主要内容如下: ... -
使用forall语句的bulk dml操作
2008-11-13 17:27 3795在oracle 8i或更高版本的forall语句中,oracl ... -
判断俩个值的大小函数sign
2008-11-10 11:13 2937比较大小select decode(sign(变量1-变量2) ... -
Oracle 触发器应用
2008-11-06 15:37 2207触发器是特定事件出现的时候,自动执行的代码块。类似于存储过程, ... -
PLSQL学习笔记
2008-10-23 16:58 2287在网上看到的一个比较好的PLSQL学习笔记,放在这里方便以后查 ... -
Oracle自定义函数实例
2008-10-22 16:35 20102--没有参数的函数 create or replace fun ... -
Oracle自定义类型使用一例
2008-10-22 16:28 4283一、创建自定义类型 create type t_air as ... -
Oracle存储过程实例
2008-10-22 15:30 3522/*不带任何参数存储过程*/ create or replac ... -
Oracle的异常处理
2008-10-22 14:40 3036oracle提供了预定义例外 ... -
识别低效的语句
2008-10-15 09:04 1470SELECT EXECUTIONS , DISK_READS, ... -
wmsys.wm_concat、sys_connect_by_path、自定义函数实现行列转换
2008-10-08 16:35 4478构建测试表: create table TABLE1 ( ... -
PL/SQL Developer使用技巧
2008-10-07 15:18 67221、PL/SQL Developer记住登陆密码 在使用 ... -
Oracle 绑定变量
2008-10-06 10:53 4386在oracle 中,对于一个提交的sql语句,存在两种可选的解 ...
相关推荐
这些子程序可以被其他PL/SQL代码调用,实现代码复用和模块化。 - **包**:包是一种封装一组相关对象(如子程序、游标、类型等)的方式,有助于提高代码的可维护性和重用性。 #### 三、PL/SQL程序数据 - **变量与...
第一章 PL-SQL一览 第二章 PL-SQL基础 第三章 PL-SQL数据类型 第四章 PL-SQL的控制结构 ...第九章 PL-SQL子程序 第十章 PL-SQL包 第十一章 PL-SQL对象类型 第十二章 本地动态SQL 第十三章 PL-SQL应用程序性能调优
共分为7部分(7个pdf文档...第一部分:PL/SQL介绍及开发环境 1.PL/SQL介绍 2.PL/SQL开发和运行环境 3.跟踪和调试 第二部分:非对象功能 4.创建子程序和包 5.使用子程序和包 6.数据库触发器 7.数据库作业和文件输入输出
《ORACLE PL/SQL 程序设计(第五版)》是Oracle数据库开发领域的一本经典教程,专注于讲解如何使用PL/SQL这一强大的过程化语言进行数据库编程。这本书旨在帮助读者掌握PL/SQL的基本概念、语法和高级特性,以便在实际...
异常处理(第8~10章) 游标(第11~12章) 触发器(第13~14章) 复合数据类型(第15~16章) 动态SQL(第17章) 批量SQL(第18章) 子程序和包(第19~22章) Oracle中对象类型(第23章) Oracle提供的包(第24章)
第八章“触发器”讲解了如何定义和使用触发器,这是一种在特定数据库事件发生时自动执行的代码,常用于实现业务规则和数据完整性约束。 最后,第九章“动态SQL”则涉及运行时构建和执行SQL语句的能力,这对于处理不...
它是SQL的扩展,允许开发者在SQL的基础上添加过程化编程元素,如循环、条件判断和子程序等,使得数据库管理与应用开发更为灵活高效。 在PL/SQL中,我们可以编写存储过程、函数、触发器、游标和异常处理等。这些组件...
#### 第八章 PL/SQL子程序 - **过程与函数**: - 过程:无返回值。 - 函数:有返回值。 - **参数传递**: - 按值传递。 - 按引用传递。 #### 第九章 PL/SQL包 - **包的定义与实现**: - 规范:声明包中的公共...
4. **子程序**:包括函数和过程。函数返回一个值,而过程不返回。它们可以封装复杂的操作,提高代码复用性。 5. **游标**:用于处理查询结果集,逐行进行操作。游标允许程序动态地控制查询的执行和结果处理。 6. *...
在"PL/SQL 8 Oracle客户端"中,我们关注的是Oracle客户端的第8个主要版本,这是在20世纪末发布的一个版本,它提供了与Oracle服务器交互的能力。 PL/SQL的主要组成部分包括变量声明、条件语句(如IF-THEN-ELSE)、...
8. **第八章 PL/SQL子程序**: - 子程序(procedures和functions)是可重用的代码块,允许创建自定义函数和过程,以实现更复杂的业务逻辑。 9. **第九章 PL/SQL包**: - 包(packages)是组织相关子程序和类型的...
标题中的"PL/SQL Develope8"可能是指该软件的第八个版本。尽管描述中提到可能存在问题,但这里我们将探讨关于PL/SQL Developer以及与其相关的PL/SQL编程语言的一些关键知识点。 1. **PL/SQL编程语言**:PL/SQL是...
目 录 第一章 PL/SQL 一览 第二章 PL/SQL 基础 第三章 PL/SQL 数据类型 ...第八章 PL/SQL 子程序 第九章 PL/SQL 包 第十章 PL/SQL 对象类型 第十一章 本地动态 SQL 第十二章 PL/SQL 应用程序性能调优
### 第八章 触发器 #### 8.1 触发器类型 触发器是PL/SQL中的另一个高级特性,用于在特定的数据库事件发生时自动执行一段代码。 - **DML触发器**: 在数据操纵语言(DML)事件(如INSERT、UPDATE、DELETE)发生时...
- **执行语法规则**:规定了PL/SQL程序的执行流程。 - **变量有效范围**:定义了变量的作用域。 - **使用SQL于PL/SQL中**:在PL/SQL中嵌入SQL语句。 - **控制流程语法规则**:如循环、条件判断等。
5. **子程序**:函数和过程是PL/SQL中的重要组成部分,用于封装可重用的代码。这一章会讲解如何定义、调用以及返回值的概念。 6. **游标**:游标允许我们逐行处理查询结果,是处理大型数据集的重要工具。这里会讲解...
在"第七章.doc"中,你可能会学习到关于PL/SQL的更高级主题,比如递归、游标的高级用法、存储过程的参数传递、复合类型以及PL/SQL与数据库并发控制的机制。每个实例都是精心设计的,旨在帮助你理解和掌握这些概念,...
3. 过程和函数:解释如何在PL/SQL中创建和使用过程和函数,包括参数传递、返回值和子程序的封装。 4. 触发器:详细说明触发器的概念、触发器的类型(行级触发器、语句级触发器)、触发器的使用场景及其对数据库事务...