plsql

更新时间:2024-05-14 10:57:01 阅读量: 综合文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

PL/SQL程序设计

1

第一章

PL/SQL 程序设计简介

PL /SQL是一种高级数据库程序设计语言,该语言专门用于在各种环境下对ORACLE数据库进行访问。由于该语言集成于数据库服务器中,所以PL/SQL代码可以对数据进行快速高效的处理。除此之外,可以在ORACLE数据库的某些客户端工具中,使用PL/SQL语言也是该语言的一个特点。本章的主要内容是讨论引入PL/SQL语言的必要性和该语言的主要特点,以及了解PL/SQL语言的重要性和数据库版本问题。还要介绍一些贯穿全书的更详细的高级概念,并在本章的最后就我们在本书案例中使用的数据库表的若干约定做一说明。

本章主要重点:

? PL/SQL概述 ? PL/SQL块结构 ? PL/SQL流程 ? 运算符和表达式 ? 游标 ? 异常处理

? 数据库存储过程和函数 ? 包 ? 触发器 §1.2 SQL与PL/SQL

§1.2.1 什么是PL/SQL?

PL/SQL是 Procedure Language & Structured Query Language 的缩写。ORACLE的SQL是支持ANSI(American national Standards Institute)和ISO92 (International Standards Organization)标准的产品。PL/SQL是对SQL语言存储过程语言的扩展。从ORACLE6以后,ORACLE的RDBMS附带了PL/SQL。它现在已经成为一种过程处理语言,简称PL/SQL。目前的PL/SQL包括两部分,一部分是数据库引擎部分;另一部分是可嵌入到许多产品(如C语言,JAVA语言等)工具中的独立引擎。可以将这两部分称为:数据库PL/SQL和工具PL/SQL。两者的编程非常相似。都具有编程结构、语法和逻辑机制。工具PL/SQL另外还增加了用于支持工具(如ORACLE Forms)的句法,如:在窗体上设置按钮等。本章主要介绍数据库PL/SQL内容。

§1.2.1 PL/SQL的好处

§1.2.1.1 有利于客户/服务器环境应用的运行

对于客户/服务器环境来说,真正的瓶颈是网络上。无论网络多快,只要客户端与服务器进行大量的数据交换。应用运行的效率自然就会受到影响。如果使用PL/SQL进行编程,将这种具有大量数据处理的应用放在服务器端来执行。自然就省去了数据在网上的传输时间。

- 1 -

2

§1.2.1.2 适合于客户环境

PL/SQL由于分为数据库PL/SQL部分和工具PL/SQL。对于客户端来说,PL/SQL可以嵌套到相应的工具中,客户端程序可以执行本地包含PL/SQL部分,也可以向服务发SQL命令或激活服务器端的PL/SQL程序运行。

§1.2.2 PL/SQL 可用的SQL语句

PL/SQL是ORACLE系统的核心语言,现在ORACLE的许多部件都是由PL/SQL写成。在PL/SQL中可以使用的SQL语句有:

INSERT,UPDATE,DELETE,SELECT INTO,COMMIT,ROLLBACK,SAVEPOINT。

提示:在 PL/SQL中只能用 SQL语句中的 DML 部分,不能用 DDL 部分,如果要在PL/SQL中使用DDL(如CREATE table 等)的话,只能以动态的方式来使用。

? ORACLE 的 PL/SQL 组件在对 PL/SQL 程序进行解释时,同时对在其所使用的

表名、列名及数据类型进行检查。 ? PL/SQL 可以在SQL*PLUS 中使用。 ? PL/SQL 可以在高级语言中使用。

? PL/SQL可以 在ORACLE的 开发工具中使用。

? 其它开发工具也可以调用PL/SQL编写的过程和函数,如Power Builder 等都可以

调用服务器端的PL/SQL过程。 §1.3 运行PL/SQL程序

PL/SQL程序的运行是通过ORACLE中的一个引擎来进行的。这个引擎可能在ORACLE的服务器端,也可能在 ORACLE 应用开发的客户端。引擎执行PL/SQL中的过程性语句,然后将SQL语句发送给数据库服务器来执行。再将结果返回给执行端。

- 2 -

3

第二章 PL/SQL块结构和组成元素

§2.1 PL/SQL块

PL/SQL程序由三个块组成,即声明部分、执行部分、异常处理部分。

PL/SQL块的结构如下:

DECLARE

/* 声明部分: 在此声明PL/SQL用到的变量,类型及游标,以及局部的存储过程和函数 */ BEGIN

/* 执行部分: 过程及SQL 语句 , 即程序的主要部分 */ EXCEPTION

/* 执行异常部分: 错误处理 */ END;

其中 执行部分是必须的。

PL/SQL块可以分为三类:

1. 无名块:动态构造,只能执行一次。

2. 子程序:存储在数据库中的存储过程、函数及包等。当在数据库上建立好后可以在其它

程序中调用它们。

3. 触发器:当数据库发生操作时,会触发一些事件,从而自动执行相应的程序。

§2.2 PL/SQL结构

? PL/SQL块中可以包含子块;

? 子块可以位于 PL/SQL中的任何部分; ? 子块也即PL/SQL中的一条命令;

§2.3 标识符

PL/SQL程序设计中的标识符定义与SQL 的标识符定义的要求相同。要求和限制有:

? 标识符名不能超过30字符; ? 第一个字符必须为字母; ? 不分大小写; ? 不能用‘-?(减号);

? 不能是SQL保留字。

提示: 一般不要把变量名声明与表中字段名完全一样,如果这样可能得到不正确的结果.

- 3 -

4

例如:下面的例子将会删除所有的纪录,而不是KING 的记录;

DECLARE

Ename varchar2(20) :=‘KING‘; BEGIN DELETE FROM emp WHERE ename=ename; END;

变量命名在PL/SQL中有特别的讲究,建议在系统的设计阶段就要求所有编程人员共同遵守一定的要求,使得整个系统的文档在规范上达到要求。下面是建议的命名方法: 标识符 程序变量 程序常量 游标变量 异常标识 表类型 表 记录类型 SQL*Plus 替代变量 绑定变量 §2.4 PL/SQL 变量类型

在前面的介绍中,有系统的数据类型,也可以自定义数据类型。下表是ORACLE类型和PL/SQL中的变量类型的合法使用列表:

§2.4.1 变量类型

在ORACLE8i中可以使用的变量类型有: 类型 子类 说 明 范 围 ORACLE限制 CHAR Character String Rowid Nchar VARCHAR2 BINARY_INTEGER NUMBER(p,s) Dec Double Varchar, String NVARCHAR2 定长字符串 民族语言字符集 可变字符串 民族语言字符集 带符号整数,为整数计算优化性能 小数, NUMBER 的子类型 高精度实数 整数, NUMBER 的子类型 0?32767 4000 4000 0?32767 可选,确省=1 2000 命名规则 V_name C_Name Name_cursor E_name Name_table_type Name_table Name_record P_name G_name V_name 例子 C_company_name Emp_cursor E_too_many Emp_record_type Emp Emp_record P_sal G_year_sal - 4 -

5

precision Integer Int Numeric Real Small int LONG DATE 整数, NUMBER 的子类型 与NUMBER等价 与NUMBER等价 整数, 比 integer 小 变长字符串 日期型 0->2147483647 公元前4712年1月1日至公元后4712年12月31日 32,767字节 BOOLEAN ROWID UROWID 布尔型 存放数据库行号 通用行标识符,字符类型 TRUE, FALSE,NULL 不使用

§2.4.2 复合类型

ORACLE 在 PL/SQL 中除了提供像前面介绍的各种类型外,还提供一种称为复合类型的类型---记录和表.

§2.4.2.1 记录类型

记录类型是把逻辑相关的数据作为一个单元存储起来,称作PL/SQL RECORD 的域(FIELD),其作用是存放互不相同但逻辑相关的信息。

定义记录类型语法如下:

TYPE record_type IS RECORD(

Field1 type1 [NOT NULL] [:= exp1 ], Field2 type2 [NOT NULL] [:= exp2 ], . . . . . .

Fieldn typen [NOT NULL] [:= expn ] ) ;

例1 :

- 5 -

6

提示: 1) DBMS_OUTPUT.PUT_LINE 过程的功能类似于 Java 中的 System.out.println() 直接将输出结果送到标准输出中.

2) 在使用上述过程之前必须将 SQL * PLUS 的环境参数 SERVEROUTPUT 设置为 ON, 否则将看不到输出结果: set serveroutput on

可以用 SELECT语句对记录变量进行赋值,只要保证记录字段与查询结果列表中的字段相配即可。

§2.4.2.2 使用%TYPE

定义一个变量,其数据类型与已经定义的某个数据变量的类型相同,或者与数据库表的某个列的数据类型相同,这时可以使用%TYPE。 使用%TYPE特性的优点在于:

? 所引用的数据库列的数据类型可以不必知道; ? 所引用的数据库列的数据类型可以实时改变。

例2:

- 6 -

7

§2.4.3 使用%ROWTYPE

PL/SQL 提供%ROWTYPE操作符, 返回一个记录类型, 其数据类型和数据库表的数据结构相一致。

使用%ROWTYPE特性的优点在于:

? 所引用的数据库中列的个数和数据类型可以不必知道; ? 所引用的数据库中列的个数和数据类型可以实时改变。

例3:

§2.4.4 PL/SQL 表(嵌套表)

PL/SQL 程序可使用嵌套表类型创建具有一个或多个列和无限行的变量, 这很像数据库中的表. 声明嵌套表类型的一般语法如下:

TYPE type_name IS TABLE OF {datatype | {variable | table.column} % type | table%rowtype}; 方法 EXISTS(n) COUNT FIRST LAST PRIOR(n) NEXT(N) TRIM 描述 Return TRUE if the nth element in a PL/SQL table exists; Returns the number of elements that a PL/SQL table currently contains; Return the first and last (smallest and lastest) index numbers in a PL/SQL table. Returns NULL if the PL/SQL table is empty. Returns the index number that precedes index n in a PL/SQL table; Returns the index number that succeeds index n in a PL/SQL table; TRIM removes one element from the end of a PL/SQL table. - 7 -

8

TRIM(n) removes n element from the end of a PL/SQL table. DELETE DELETE removes all elements from a PL/SQL table. DELETE(n) removes the nth elements from a PL/SQL table. DELETE(m, n) removes all elements in the range m to n from a PL/SQL table. 例4:

说明: 1) 在使用嵌套表之前必须先使用该集合的构造器初始化它. PL/SQL 自动提供一个带有

相同名字的构造器作为集合类型.

2) 嵌套表可以有任意数量的行. 表的大小在必要时可动态地增加或减少: extend(x) 方

法添加 x 个空元素到集合末尾; trim(x) 方法为去掉集合末尾的 x 个元素. §2.5 运算符和表达式(数据定义) §2.5.1 关系运算符

运算符 = <> , != , ~= , ^= < > <= >=

§2.5.2 一般运算符

运算符 + 意义 加号 意义 等于 不等于 小于 大于 小于或等于 大于或等于 - 8 -

9

- * / := => .. ||

§2.5.3 逻辑运算符

运算符 IS NULL BETWEEN AND IN AND OR NOT

§2.6 变量赋值

减号 乘号 除号 赋值号 关系号 范围运算符 字符连接符 意义 是空值 介于两者之间 在一列值中间 逻辑与 逻辑或 取返,如IS NOT NULL, NOT IN 在PL/SQL编程中,变量赋值是一个值得注意的地方,它的语法如下: variable := expression ;

variable 是一个PL/SQL变量, expression 是一个PL/SQL 表达式.

§2.6.1 字符及数字运算特点

空值加数字仍是空值:NULL + < 数字> = NULL

空值加(连接)字符,结果为字符:NULL || <字符串> = < 字符串>

§2.6.2 BOOLEAN 赋值

布尔值只有TRUE, FALSE及 NULL 三个值。

§2.6.3 数据库赋值

数据库赋值是通过 SELECT语句来完成的,每次执行 SELECT语句就赋值一次,一般要求被赋值的变量与SELECT中的列名要一一对应。如: 例9:

DECLARE

emp_id emp.empno%TYPE :=7788;

- 9 -

10

emp_name emp.ename%TYPE; wages emp.sal%TYPE; BEGIN

SELECT ename, NVL(sal,0) + NVL(comm,0) INTO emp_name, wages

FROM emp WHERE empno = emp_id;

DBMS_OUTPUT.PUT_LINE(emp_name||‘----?||to_char(wages)); END;

提示:不能将SELECT语句中的列赋值给布尔变量。

§2.6.4 可转换的类型赋值

? CHAR 转换为 NUMBER:

使用 TO_NUMBER 函数来完成字符到数字的转换,如: v_total := TO_NUMBER(?100.0‘) + sal;

? NUMBER 转换为CHAR:

使用 TO_CHAR函数可以实现数字到字符的转换,如:

v_comm := TO_CHAR(?123.45‘) || ‘元‘ ;

? 字符转换为日期:

使用 TO_DATE函数可以实现 字符到日期的转换,如: v_date := TO_DATE('2001.07.03','yyyy.mm.dd');

? 日期转换为字符

使用 TO_CHAR函数可以实现日期到字符的转换,如:

v_to_day := TO_CHAR(SYSDATE, 'yyyy.mm.dd hh24:mi:ss') ;

§2.7 变量作用范围及可见性

在PL/SQL编程中,如果在变量的定义上没有做到统一的话,可能会隐藏一些危险的错误,这样的原因主要是变量的作用范围所致。与其它高级语言类似,PL/SQL的变量作用范围特点是:

? 变量的作用范围是在你所引用的程序单元(块、子程序、包)内。即从声明变量开

始到该块的结束。

? 一个变量(标识)只能在你所引用的块内是可见的。

? 当一个变量超出了作用范围,PL/SQL引擎就释放用来存放该变量的空间(因为它

可能不用了)。

? 在子块中重新定义该变量后,它的作用仅在该块内。

- 10 -

11

§2.8 注释

在PL/SQL里,可以使用两种符号来写注释,即:

? 使用双 ‘-‘ ( 减号) 加注释

PL/SQL允许用 – 来写注释,它的作用范围是只能在一行有效。如: V_Sal NUMBER(12,2); -- 工资变量。

? 使用 /* */ 来加一行或多行注释,如: /***********************************************/ /* 文件名: department_salary.sql */ /***********************************************/

提示:被解释存放在数据库中的 PL/SQL 程序,一般系统自动将程序头部的注释去掉。只有在 PROCEDURE 之后的注释才被保留;另外程序中的空行也自动被去掉。 §2.9 简单例子

§2.9.1 简单数据插入例子

例11:

/* 本例子仅是一个简单的插入,不是实际应用。 */ DECLARE

v_ename VARCHAR2(20) := ?Bill‘; v_sal NUMBER(7,2) :=1234.56; v_deptno NUMBER(2) := 10; v_empno NUMBER(4) := 8888; BEGIN

INSERT INTO emp ( empno, ename, JOB, sal, deptno , hiredate )

VALUES ( v_empno, v_ename, ?Manager‘, v_sal, v_deptno, TO_DATE(‘1954.06.09‘,‘yyyy.mm.dd‘) ); COMMIT; END;

§2.9.2 简单数据删除例子 例12:

/* 本例子仅是一个简单的删除例子,不是实际应用。 */ DECLARE

v_empno number(4) := 8888; BEGIN

DELETE FROM emp WHERE empno=v_empno; COMMIT; END;

- 11 -

12

第三章 PL/SQL流程控制语句

介绍PL/SQL的流程控制语句, 包括如下三类: ? 控制语句: IF 语句

? 循环语句: LOOP语句, EXIT语句 ? 顺序语句: GOTO语句, NULL语句 §3.1 条件语句

IF <布尔表达式> THEN PL/SQL 和 SQL语句 END IF;

IF <布尔表达式> THEN PL/SQL 和 SQL语句 ELSE

其它语句 END IF;

IF <布尔表达式> THEN

PL/SQL 和 SQL语句

ELSIF < 其它布尔表达式> THEN

其它语句

ELSIF < 其它布尔表达式> THEN

其它语句 ELSE

其它语句 END IF;

提示: ELSIF 不能写成 ELSEIF

例1:

DECLARE

v_empno emp.empno%TYPE; V_salary emp.sal%TYPE; V_comment VARCHAR2(35); BEGIN

SELECT sal INTO v_salary FROM emp WHERE empno=v_empno; IF v_salary<1500 THEN

V_comment:= ?Fairly less‘; ELSIF v_salary <3000 THEN V_comment:= ?A little more‘;

- 12 -

13

ELSE

V_comment:= ?Lots of salary‘; END IF;

DBMS_OUTPUT.PUT_LINE(V_comment); END;

§3.2 CASE 表达式

CASE selector WHEN expression1 THEN result1 WHEN expression2 THEN result2 WHEN expressionN THEN resultN [ ELSE resultN+1] END;

例2:

DECLARE V_grade char(1) ; V_appraisal VARCHAR2(20); BEGIN V_appraisal := CASE v_grade WHEN ?A‘ THEN ?Excellent‘ WHEN ?B‘ THEN ?Very Good‘ WHEN ?C‘ THEN ?Good‘ ELSE ?No such grade‘ END; DBMS_OUTPUT.PUT_LINE(?Grade:?||v_grade||‘ Appraisal: ?|| v_appraisal); END;

§3.3 循环

1. 简单循环

LOOP

要执行的语句;

EXIT WHEN <条件语句> /*条件满足,退出循环语句*/

END LOOP;

- 13 -

14

例 3.

DECLARE

int NUMBER(2) :=0; BEGIN LOOP

int := int + 1;

DBMS_OUTPUT.PUT_LINE('int 的当前值为:'||int); EXIT WHEN int =10; END LOOP; END;

2. WHILE 循环

WHILE <布尔表达式> LOOP 要执行的语句; END LOOP;

例4.

DECLARE

x NUMBER :=1; BEGIN

WHILE x<=10 LOOP

DBMS_OUTPUT.PUT_LINE('X的当前值为:'||x); x:= x+1; END LOOP; END;

3. 数字式循环

FOR 循环计数器 IN [ REVERSE ] 下限 .. 上限 LOOP 要执行的语句; END LOOP;

每循环一次,循环变量自动加1;使用关键字REVERSE,循环变量自动减1。跟在IN REVERSE 后面的数字必须是从小到大的顺序,而且必须是整数,不能是变量或表达式。可以使用EXIT 退出循环。

例5. BEGIN

FOR int in 1..10 LOOP

DBMS_OUTPUT.PUT_LINE('int 的当前值为: '||int); END LOOP; END;

例 6.

- 14 -

15

CREATE TABLE temp_table(num_col NUMBER);

DECLARE

V_counter NUMBER := 10; BEGIN

INSERT INTO temp_table(num_col) VALUES (v_counter ); FOR v_counter IN 20 .. 25 LOOP

INSERT INTO temp_table (num_col ) VALUES ( v_counter ); END LOOP;

INSERT INTO temp_table(num_col) VALUES (v_counter ); FOR v_counter IN REVERSE 20 .. 25 LOOP

INSERT INTO temp_table (num_col ) VALUES ( v_counter ); END LOOP; END ;

DROP TABLE temp_table; §3.3 标号和GOTO

PL/SQL中GOTO语句是无条件跳转到指定的标号去的意思。语法如下:

GOTO label; . . . . . .

<

例7:

DECLARE

V_counter NUMBER := 1; BEGIN LOOP

DBMS_OUTPUT.PUT_LINE('V_counter的当前值为:'||V_counter);

V_counter := v_counter + 1; IF v_counter > 10 THEN GOTO l_ENDofLOOP; END IF; END LOOP;

<>

DBMS_OUTPUT.PUT_LINE('V_counter的当前值为:'||V_counter); END ;

§3.4 NULL 语句

在PL/SQL 程序中,可以用 null 语句来说明―不用做任何事情‖的意思,相当于一个占位符,可以使某些语句变得有意义,提高程序的可读性。如:

- 15 -

16

DECLARE

. . . BEGIN

IF v_num IS NULL THEN

GOTO print1; END IF; …

<>

NULL; END;

-- 不需要处理任何数据。- 16 -

17

第四章 游标的使用

在 PL/SQL 程序中,对于处理多行记录的事务经常使用游标来实现。 §4.1 游标概念

为了处理 SQL 语句,ORACLE 必须分配一片叫上下文( context area )的区域来处理所必需的信息,其中包括要处理的行的数目,一个指向语句被分析以后的表示形式的指针以及查询的活动集(active set)。

游标是一个指向上下文的句柄( handle)或指针。通过游标,PL/SQL可以控制上下文区和处理语句时上下文区会发生些什么事情。 对于不同的SQL语句,游标的使用情况不同: SQL语句 非查询语句 结果是单行的查询语句 结果是多行的查询语句 隐式的 隐式的或显示的 显示的 游标

§4.1.1 处理显式游标

1. 显式游标处理

显式游标处理需四个 PL/SQL步骤:

? 定义游标:就是定义一个游标名,以及与其相对应的SELECT 语句。

格式:

CURSOR cursor_name[(parameter[, parameter]…)] IS select_statement; 游标参数只能为输入参数,其格式为: parameter_name [IN] datatype [{:= | DEFAULT} expression] 在指定数据类型时,不能使用长度约束。如NUMBER(4)、CHAR(10) 等都是错误的。 ? 打开游标:就是执行游标所对应的SELECT 语句,将其查询结果放入工作区,并且指

针指向工作区的首部,标识游标结果集合。如果游标查询语句中带有FOR UPDATE选项,OPEN 语句还将锁定数据库表中游标结果集合对应的数据行。 格式:

OPEN cursor_name[([parameter =>] value[, [parameter =>] value]…)]; 在向游标传递参数时,可以使用与函数参数相同的传值方法,即位置表示法和名称表示 法。PL/SQL 程序不能用OPEN 语句重复打开一个游标。

? 提取游标数据:就是检索结果集合中的数据行,放入指定的输出变量中。

格式:

FETCH cursor_name INTO {variable_list | record_variable }; ? 对该记录进行处理;

? 继续处理,直到活动集合中没有记录;

? 关闭游标:当提取和处理完游标结果集合数据后,应及时关闭游标,以释放该游标所占

用的系统资源,并使该游标的工作区变成无效,不能再使用FETCH 语句取其中数据。关闭后的游标可以使用OPEN 语句重新打开。 格式:

CLOSE cursor_name;

- 17 -

18

注:定义的游标不能有INTO 子句。

例1. 查询前10名员工的信息。

例2. 游标参数的传递方法。

- 18 -

19

2.游标属性

%FOUND 布尔型属性,当最近一次读记录时成功返回,则值为TRUE; %NOTFOUND 布尔型属性,与%FOUND相反;

%ISOPEN 布尔型属性,当游标已打开时返回 TRUE; %ROWCOUNT 数字型属性,返回已从游标中读取的记录数。

例3:给工资低于3000 的员工工资调为 3000。

- 19 -

20

3. 游标的FOR循环

PL/SQL语言提供了游标FOR循环语句,自动执行游标的OPEN、FETCH、CLOSE语句和循环语句的功能;当进入循环时,游标FOR循环语句自动打开游标,并提取第一行游标数据,当程序处理完当前所提取的数据而进入下一次循环时,游标FOR循环语句自动提取下一行数据供程序处理,当提取完结果集合中的所有数据行后结束循环,并自动关闭游标。 格式: FOR index_variable IN cursor_name[value[, value]…] LOOP -- 游标数据处理代码 END LOOP; 其中: index_variable为游标FOR 循环语句隐含声明的索引变量,该变量为记录变量,其结构与游标查询语句返回的结构集合的结构相同。在程序中可以通过引用该索引记录变量元素来读取所提取的游标数据,index_variable中各元素的名称与游标查询语句选择列表中所制定的列名相同。如果在游标查询语句的选择列表中存在计算列,则必须为这些计算列指定别名后才能通过游标FOR 循环语句中的索引变量来访问这些列数据。

注:不要在程序中对游标进行人工操作;不要在程序中定义用于控制FOR 循环的记录。

例4:

例5:当所声明的游标带有参数时,通过游标FOR 循环语句为游标传递参数。

- 20 -

21

例6:PL/SQL还允许在游标FOR循环语句中使用子查询来实现游标的功能。

§4.1.2 处理隐式游标

显式游标主要是用于对查询语句的处理,尤其是在查询结果为多条记录的情况下;而对于非查询语句,如修改、删除操作,则由ORACLE 系统自动地为这些操作设置游标并创建其工作区,这些由系统隐含创建的游标称为隐式游标,隐式游标的名字为SQL,这是由ORACLE 系统定义的。对于隐式游标的操作,如定义、打开、取值及关闭操作,都由ORACLE 系统自动地完成,无需用户进行处理。用户只能通过隐式游标的相关属性,来完成相应的操作。在隐式游标的工作区中,所存放的数据是与用户自定义的显示游标无关的、最新处理的一条SQL 语句所包含的数据。

格式调用为: SQL%

隐式游标属性

SQL%FOUND 布尔型属性,当最近一次读记录时成功返回,则值为TRUE; SQL%NOTFOUND 布尔型属性,与%FOUND相反;

SQL %ROWCOUNT 数字型属性, 返回已从游标中读取得记录数; SQL %ISOPEN 布尔型属性, 取值总是FALSE。SQL命令执行完毕立即关闭隐式游标。

例7: 更新指定员工信息,如果该员工没有找到,则打印‖查无此人‖信息。

- 21 -

22

§4.1.3 关于 NO_DATA_FOUND 和 %NOTFOUND的区别

SELECT … INTO 语句触发 NO_DATA_FOUND;

当一个显式游标的WHERE子句未找到时触发%NOTFOUND;

当UPDATE或DELETE 语句的WHERE 子句未找到时触发 SQL%NOTFOUND;在提取循环中要用 %NOTFOUND 或%FOUND 来确定循环的退出条件,不要用 NO_DATA_FOUND.

§4.1.4 游标修改和删除操作

游标修改和删除操作是指在游标定位下,修改或删除表中指定的数据行。这时,要求游标查询语句中必须使用FOR UPDATE选项,以便在打开游标时锁定游标结果集合在表中对应数据行的所有列和部分列。

为了对正在处理(查询)的行不被另外的用户改动,ORACLE 提供一个 FOR UPDATE 子句来对所选择的行进行锁住。该需求迫使ORACLE锁定游标结果集合的行,可以防止其他事务处理更新或删除相同的行,直到您的事务处理提交或回退为止。

语法:

SELECT . . . FROM … FOR UPDATE [OF column[, column]…] [NOWAIT]

如果另一个会话已对活动集中的行加了锁,那么SELECT FOR UPDATE操作一直等待到其它的会话释放这些锁后才继续自己的操作,对于这种情况,当加上NOWAIT子句时,如果这些行真的被另一个会话锁定,则OPEN立即返回并给出: ORA-0054 :resource busy and acquire with nowait specified.

如果使用 FOR UPDATE 声明游标,则可在DELETE和UPDATE 语句中使用WHERE CURRENT OF cursor_name子句,修改或删除游标结果集合当前行对应的数据库表中的数据行。

例8:从EMPLOYEES表中查询某部门的员工情况,将其工资最低定为 3000;

- 22 -

23

- 23 -

24

第五章 异常错误处理

一个优秀的程序都应该能够正确处理各种出错情况,并尽可能从错误中恢复。ORACLE 提供异常情况(EXCEPTION)和异常处理(EXCEPTION HANDLER)来实现错误处理。 §5.1 异常处理概念

异常情况处理(EXCEPTION)是用来处理正常执行过程中未预料的事件,程序块的异常处理预定义的错误和自定义错误,由于PL/SQL程序块一旦产生异常而没有指出如何处理时,程序就会自动终止整个程序运行.

有三种类型的异常错误: 1. 预定义 ( Predefined )错误

ORACLE预定义的异常情况大约有24个。对这种异常情况的处理,无需在程序中定义,由ORACLE自动将其引发。 2. 非预定义 ( Predefined )错误

即其他标准的ORACLE错误。对这种异常情况的处理,需要用户在程序中定义,然后由ORACLE自动将其引发。 3. 用户定义(User_define) 错误

程序执行过程中,出现编程人员认为的非正常情况。对这种异常情况的处理,需要用户在程序中定义,然后显式地在程序中将其引发。

异常处理部分一般放在 PL/SQL 程序体的后半部,结构为:

EXCEPTION

WHEN first_exception THEN

WHEN second_exception THEN WHEN OTHERS THEN END;

异常处理可以按任意次序排列,但 OTHERS 必须放在最后.

§5.1.1 预定义的异常处理

预定义说明的部分 ORACLE 异常错误 错误号 ORA-0001 ORA-0051 ORA-0061 ORA-1001 ORA-1012 ORA-1017 ORA-1403 ORA-1422 异常错误信息名称 Dup_val_on_index Timeout-on-resource Transaction-backed-out Invalid-CURSOR Not-logged-on Login-denied No_data_found Too_many_rows 说明 试图破坏一个唯一性限制 在等待资源时发生超时 由于发生死锁事务被撤消 试图使用一个无效的游标 没有连接到ORACLE 无效的用户名/口令 SELECT INTO没有找到数据 SELECT INTO 返回多行 - 24 -

25

ORA-1476 ORA-1722 ORA-6500 ORA-6501 ORA-6502 ORA-6504 ORA-6511 ORA-6530 ORA-6531 ORA-6532 ORA-6533 Zero-divide Invalid-NUMBER Storage-error Program-error Value-error Rowtype-mismatch CURSOR-already-OPEN Access-INTO-null Collection-is-null Subscript-outside-limit Subscript-beyond-count 试图被零除 转换一个数字失败 内存不够引发的内部错误 内部错误 转换或截断错误 宿主游标变量与 PL/SQL变量有不兼容行类型 试图打开一个已存在的游标 试图为null 对象的属性赋值 试图将Exists 以外的集合( collection)方法应用于一个null pl/sql 表上或varray上 对嵌套或varray索引得引用超出声明范围以外 对嵌套或varray 索引得引用大于集合中元素的个数. 对这种异常情况的处理,只需在PL/SQL块的异常处理部分,直接引用相应的异常情况名,并对其完成相应的异常错误处理即可。

例1:更新指定员工工资,如工资小于300,则加100;对 NO_DATA_FOUND 异常, TOO_MANY_ROWS 进行处理.

§5.1.2 非预定义的异常处理

对于这类异常情况的处理,首先必须对非定义的ORACLE错误进行定义。步骤如下: 1. 在PL/SQL 块的定义部分定义异常情况:

- 25 -

26

<异常情况> EXCEPTION;

2. 将其定义好的异常情况,与标准的ORACLE错误联系起来,使用EXCEPTION_INIT

语句:

PRAGMA EXCEPTION_INIT(<异常情况>, <错误代码>);

3. 在PL/SQL 块的异常情况处理部分对异常情况做出相应的处理。

例2:删除指定部门的记录信息,以确保该部门没有员工。

§5.1.3 用户自定义的异常处理

当与一个异常错误相关的错误出现时,就会隐含触发该异常错误。用户定义的异常错误是通过显式使用 RAISE 语句来触发。当引发一个异常错误时,控制就转向到 EXCEPTION块异常错误部分,执行错误处理代码。

对于这类异常情况的处理,步骤如下:

1. 在PL/SQL 块的定义部分定义异常情况:

<异常情况> EXCEPTION;

2. RAISE <异常情况>;

3. 在PL/SQL 块的异常情况处理部分对异常情况做出相应的处理。

例3:更新指定员工工资,增加100;若该员工不存在则抛出用户自定义异常: no_result

- 26 -

27

§5.2 在 PL/SQL 中使用 SQLCODE, SQLERRM

SQLCODE 返回错误代码数字 SQLERRM 返回错误信息.

如: SQLCODE=-100 ? SQLERRM=‘no_data_found ?

SQLCODE=0 ? SQLERRM=‘normal, successfual completion‘

例5. 将ORACLE错误代码及其信息存入错误代码表

CREATE TABLE errors (errnum NUMBER(4), errmsg VARCHAR2(100));

DECLARE

err_msg VARCHAR2(100); BEGIN

/* 得到所有 ORACLE 错误信息 */ FOR err_num IN -100 .. 0 LOOP

err_msg := SQLERRM(err_num);

INSERT INTO errors VALUES(err_num, err_msg); END LOOP; END;

DROP TABLE errors;

例6. 查询ORACLE错误代码; BEGIN

INSERT INTO emp(empno, ename, hiredate, deptno) VALUES(2222, ?Jerry‘, SYSDATE, 20);

DBMS_OUTPUT.PUT_LINE('插入数据记录成功!'); INSERT INTO emp(empno, ename, hiredate, deptno)

- 27 -

28

VALUES(2222, ?Jerry‘, SYSDATE, 20);

DBMS_OUTPUT.PUT_LINE('插入数据记录成功!'); EXCEPTION

WHEN OTHERS THEN

DBMS_OUTPUT.PUT_LINE(SQLCODE||‘---?||SQLERRM); END;

- 28 -

29

第六章 存储函数和过程

§6.1 引言

1. ORACLE 提供可以把PL/SQL 程序存储在数据库中,并可以在任何地方来运行它。这样就叫存储过程或函数。过程和函数统称为PL/SQL子程序,他们是被命名的PL/SQL块,均存储在数据库中,并通过输入、输出参数或输入/输出参数与其调用者交换信息。过程和函数的唯一区别是函数总向调用者返回数据,而过程则不返回数据。 §6.2 创建函数

1. 建立内嵌函数 语法如下:

CREATE [OR REPLACE] FUNCTION function_name

[ (argment [ { IN | IN OUT }] Type, argment [ { IN | OUT | IN OUT } ] Type ] [ AUTHID DEFINER | CURRENT_USER ] RETURN return_type { IS | AS }

<类型.变量的说明> BEGIN

FUNCTION_body EXCEPTION

其它语句 END;

说明:

1) OR REPLACE 为可选. 有了它, 可以或者创建一个新函数或者替换相同名字的函数,

而不会出现冲突

2) 函数名后面是一个可选的参数列表, 其中包含 IN, OUT 或 IN OUT 标记. 参数之间用

逗号隔开. IN 参数标记表示传递给函数的值在该函数执行中不改变; OUT 标记表示一个值在函数中进行计算并通过该参数传递给调用语句; IN OUT 标记表示传递给函数的值可以变化并传递给调用语句. 若省略标记, 则参数隐含为 IN。

3) 因为函数需要返回一个值, 所以 RETURN 包含返回结果的数据类型.

例1. 不带参数的函数

- 29 -

30

执行该函数

例2.

获取某部门的工资总和:

2. 内嵌函数的调用

函数声明时所定义的参数称为形式参数,应用程序调用时为函数传递的参数称为实际参数。应用程序在调用函数时,可以使用以下三种方法向函数传递参数:

第一种参数传递格式称为位置表示法,格式为:

- 30 -

31

argument_value1[,argument_value2 …]

例3:计算某部门的工资总和:

第二种参数传递格式称为名称表示法,格式为: argument => parameter [,…] 其中:argument 为形式参数,它必须与函数定义时所声明的形式参数名称相同。Parameter 为实际参数。

在这种格式中,形势参数与实际参数成对出现,相互间关系唯一确定,所以参数的顺序可以任意排列。

例4:计算某部门的工资总和:

第三种参数传递格式称为混合表示法:

即在调用一个函数时,同时使用位置表示法和名称表示法为函数传递参数。采用这种参数传递方法时,使用位置表示法所传递的参数必须放在名称表示法所传递的参数前面。也就是说,无论函数具有多少个参数,只要其中有一个参数使用名称表示法,其后所有的参数都必须使用名称表示法。

例5:

- 31 -

32

无论采用哪一种参数传递方法,实际参数和形式参数之间的数据传递只有两种方法:传址法和传值法。所谓传址法是指在调用函数时,将实际参数的地址指针传递给形式参数,使形式参数和实际参数指向内存中的同一区域,从而实现参数数据的传递。这种方法又称作参照法,即形式参数参照实际参数数据。输入参数均采用传址法传递数据。 传值法是指将实际参数的数据拷贝到形式参数,而不是传递实际参数的地址。默认时,输出参数和输入/输出参数均采用传值法。在函数调用时,ORACLE将实际参数数据拷贝到输入/输出参数,而当函数正常运行退出时,又将输出形式参数和输入/输出形式参数数据拷贝到实际参数变量中。

3. 参数默认值

在CREATE OR REPLACE FUNCTION 语句中声明函数参数时可以使用DEFAULT关键字为输入参数指定默认值。

例6:

具有默认值的函数创建后,在函数调用时,如果没有为具有默认值的参数提供实际参数值,函数将使用该参数的默认值。但当调用者为默认参数提供实际参数时,函数将使用实际参数值。在创建函数时,只能为输入参数设置默认值,而不能为输入/输出参数设置默认值。

- 32 -

33

§6.3 存储过程

§6.3.1 创建过程

建立存储过程

在 ORACLE SERVER上建立存储过程,可以被多个应用程序调用,可以向存储过程传递参数,也可以向存储过程传回参数.

创建过程语法:

CREATE [OR REPLACE] PROCEDURE Procedure_name

[ (argment [ { IN | IN OUT }] Type, argment [ { IN | OUT | IN OUT } ] Type ] [ AUTHID DEFINER | CURRENT_USER ] { IS | AS }

<类型.变量的说明> BEGIN

<执行部分> EXCEPTION

<可选的异常错误处理程序> END;

例7.删除指定员工记录;

- 33 -

34

例8.插入员工记录;

§6.3.2 调用存储过程

ORACLE 使用EXECUTE 语句来实现对存储过程的调用:

EXEC[UTE] Procedure_name( parameter1, parameter2…);

例9:查询指定员工记录;

- 34 -

35

调用方法:

例10.计算指定部门的工资总和,并统计其中的职工数量。

- 35 -

36

调用方法:

§6.3.3 AUTHID

在创建存储过程时, 可使用 AUTHID CURRENT_USER 或 AUTHID DEFINER 选项,以表明在执行该过程时 Oracle 使用的权限.

1) 如果使用 AUTHID CURRENT_USER 选项创建一个过程, 则 Oracle 用调用

该过程的用户权限执行该过程. 为了成功执行该过程, 调用者必须具有访问该存储过程体中引用的所有数据库对象所必须的权限

2) 如果用默认的 AUTHID DEFINER 选项创建过程, 则 Oracle 使用过程所有

者的特权执行该过程. 为了成功执行该过程, 过程的所有者必须具有访问该存储过程体中引用的所有数据库对象所必须的权限. 想要简化应用程序用户的特权管理, 在创建存储过程时, 一般选择 AUTHID DEFINER 选项 –-- 这样就不必授权给需要调用的此过程的所有用户了.

§6.3.4 开发存储过程步骤

开发存储过程、函数、包及触发器的步骤如下:

§6.3.4.1 使用文字编辑处理软件编辑存储过程源码 使用文字编辑处理软件编辑存储过程源码,,需将源码存为文本格式。

§6.3.4.2 在SQLPLUS或用调试工具将存储过程程序进行解释 在SQLPLUS或用调试工具将存储过程程序进行解释;

在SQL>下调试,可用START 或GET 等ORACLE命令来启动解释。如: SQL>START c:\\stat1.sql

§6.3.4.3 调试源码直到正确

我们不能保证所写的存储过程达到一次就正确。所以这里的调式是每个程序员必须进行的工作之一。在SQLPLUS下来调式主要用的方法是: ? 使用 SHOW ERROR命令来提示源码的错误位置;

? 使用 user_errors 数据字典来查看各存储过程的错误位置。

§6.3.4.4 授权执行权给相关的用户或角色

如果调式正确的存储过程没有进行授权,那就只有建立者本人才可以运行。所以作为应用系统的一部分的存储过程也必须进行授权才能达到要求。在SQL*PLUS下可以用GRANT

- 36 -

37

命令来进行存储过程的运行授权。

GRANT EXECUTE ON dbms_job TO PUBLIC WITH GRANT OPTION

§6.3.4.5 与过程相关数据字典

USER_SOURCE, ALL_SOURCE, DBA_SOURCE, USER_ERRORS

相关的权限:

CREATE ANY PROCEDURE DROP ANY PROCEDURE

在SQL*PLUS 中,可以用DESCRIBE 命令查看过程的名字及其参数表。

DESCRIBE Procedure_name;

§6.3.5 删除过程和函数

1.删除过程

可以使用DROP PROCEDURE命令对不需要的过程进行删除,语法如下:

DROP PROCEDURE [user.]Procudure_name;

2.删除函数

可以使用DROP FUNCTION 命令对不需要的函数进行删除,语法如下:

DROP FUNCTION [user.]Function_name;

- 37 -

38

第七章 包的创建和应用

§7.1 引言

包是一组相关过程、函数、变量、常量和游标等PL/SQL程序设计元素的组合,它具有面向对象程序设计语言的特点,是对这些PL/SQL 程序设计元素的封装。包类似于C++和JAVA语言中的类,其中变量相当于类中的成员变量,过程和函数相当于类方法。把相关的模块归类成为包,可使开发人员利用面向对象的方法进行存储过程的开发,从而提高系统性能。 与类相同,包中的程序元素也分为公用元素和私用元素两种,这两种元素的区别是他们允许访问的程序范围不同,即它们的作用域不同。公用元素不仅可以被包中的函数、过程所调用,也可以被包外的PL/SQL程序访问,而私有元素只能被包内的函数和过程序所访问。 在PL/SQL程序设计中,使用包不仅可以使程序设计模块化,对外隐藏包内所使用的信息(通过使用私用变量),而且可以提高程序的执行效率。因为,当程序首次调用包内函数或过程时,ORACLE将整个包调入内存,当再次访问包内元素时,ORACLE直接从内存中读取,而不需要进行磁盘I/O操作,从而使程序执行效率得到提高。

一个包由两个分开的部分组成: 包定义(PACKAGE):包定义部分声明包内数据类型、变量、常量、游标、子程序和异常错误处理等元素,这些元素为包的公有元素。 包主体(PACKAGE BODY):包主体则是包定义部分的具体实现,它定义了包定义部分所声明的游标和子程序,在包主体中还可以声明包的私有元素。 包定义和包主体分开编译,并作为两部分分开的对象存放在数据库字典中,详见数据字典user_source, all_source, dba_source. §7.2 包的定义

包定义的语法如下: 创建包定义:

CREATE [OR REPLACE] PACKAGE package_name

[AUTHID {CURRENT_USER | DEFINER}] {IS | AS}

[公有数据类型定义[公有数据类型定义]…] [公有游标声明[公有游标声明]…]

[公有变量、常量声明[公有变量、常量声明]…] [公有子程序声明[公有子程序声明]…] END [package_name];

其中:AUTHID CURRENT_USER和AUTHID DEFINER选项说明应用程序在调用函数时所使用的权限模式,它们与CREATE FUNCTION语句中invoker_right_clause子句的作用相同。

创建包主体:

- 38 -

39

CREATE [OR REPLACE] PACKAGE BODY package_name

{IS | AS}

[私有数据类型定义[私有数据类型定义]…] [私有变量、常量声明[私有变量、常量声明]…] [私有子程序声明和定义[私有子程序声明和定义]…] [公有游标定义[公有游标定义]…] [公有子程序定义[公有子程序定义]…] BEGIN

PL/SQL 语句

END [package_name];

其中:在包主体定义公有程序时,它们必须与包定义中所声明子程序的格式完全一致。 §7.3 包的开发步骤

与开发存储过程类似,包的开发需要几个步骤: 1. 将每个存储过程调式正确;

2. 用文本编辑软件将各个存储过程和函数集成在一起; 3. 按照包的定义要求将集成的文本的前面加上包定义; 4. 按照包的定义要求将集成的文本的前面加上包主体; 5. 使用SQLPLUS或开发工具进行调式。 §7.4 包定义的说明

例1:创建的包为demo_pack, 该包中包含一个记录变量DeptRec、两个函数和一个过程。

CREATE OR REPLACE PACKAGE demo_pack

IS DeptRec dept%ROWTYPE; FUNCTION add_dept(

dept_no NUMBER, dept_name VARCHAR2, location VARCHAR2) RETURN NUMBER; FUNCTION remove_dept(dept_no NUMBER) RETURN NUMBER; PROCEDURE query_dept(dept_no IN NUMBER); END demo_pack;

包主体的创建方法,它实现上面所声明的包定义

CREATE OR REPLACE PACKAGE BODY demo_pack IS

FUNCTION add_dept

(dept_no NUMBER, dept_name VARCHAR2, location VARCHAR2)

- 39 -

本文来源:https://www.bwwdw.com/article/2w97.html

Top