1、PL/SQL简介
Oracle PL/SQL 语言(Procedural Language/SQL)是结合了结构化查询与 Oracle 自身过程控制为一体的强大语言,PL/SQL 不但支持更多的数据类型,拥有自身的变量声明、赋值语句,而且还有条件、循环等流程控制语句。过程控制结构与 SQL 数据处理能力无缝的结合形成了强大的编程语言,可以创建过程和函数以及程序包。
Oracle PL/SQL 语言(Procedural Language/SQL)是结合了结构化查询与 Oracle 自身过程控制为一体的强大语言,PL/SQL 不但支持更多的数据类型,拥有自身的变量声明、赋值语句,而且还有条件、循环等流程控制语句。过程控制结构与 SQL 数据处理能力无缝的结合形成了强大的编程语言,可以创建过程和函数以及程序包。
PL/SQL 块发送给服务器后,先被编译然后执行,对于有名称的 PL/SQL 块(如子程序)可以单独编译,永久的存储在数据库中,随时准备执行。PL/SQL 的优点还有:
- 支持 SQL:SQL 是访问数据库的标准语言,通过 SQL 命令,用户可以操纵数据库中的数据。PL/SQL支持所有的 SQL 数据操纵命令、游标控制命令、事务控制命令、SQL 函数、运算符和伪列。同时 PL/SQL 和 SQL 语言紧密集成,PL/SQL 支持所有的 SQL 数据类型和 NULL 值。
- 支持面向对象编程:PL/SQL 支持面向对象的编程,在 PL/SQL 中可以创建类型,可以对类型进行继承,可以在子程序中重载方法等。
- 更好的性能:SQL 是非过程语言,只能一条一条执行,而 PL/SQL 把一个 PL/SQL 块统一进行编译后执行,同时还可以把编译好的 PL/SQL 块存储起来,以备重用,减少了应用程序和服务器之间的通信时间,PL/SQL 是快速而高效的。
- 可移植性:使用 PL/SQL 编写的应用程序,可以移植到任何操作系统平台上的 Oracle 服务器,同时还可以编写可移植程序库,在不同环境中重用。
- 安全性:可以通过存储过程对客户机和服务器之间的应用程序逻辑进行分隔,这样可以限制对Oracle 数据库的访问,数据库还可以授权和撤销其他用户访问的能力。
2. PL/SQL 块
PL/SQL 是一种块结构的语言,一个 PL/SQL 程序包含了一个或者多个逻辑块,逻辑块中可以声明变量,变量在使用之前必须先声明。除了正常的执行程序外,PL/SQL 还提供了专门的异常处理部分进行异常处理。每个逻辑块分为三个部分,语法是:
语法结构:PL/SQL 块的语法
[DECLARE
--declaration statements]
BEGIN
--executable statements
[EXCEPTION
--exception statements]
END;
语法解析:
- 声明部分:声明部分包含了变量和常量的定义。这个部分由关键字 DECLARE 开始,如果不声明变量或者常量,可以省略这部分。
- 执行部分:执行部分是 PL/SQL 块的指令部分,由关键字 BEGIN 开始,关键字 END结尾。所有的可执行 PL/SQL 语句都放在这一部分,该部分执行命令并操作变量。其他的 PL/SQL 块可以作为子块嵌套在该部分。PL/SQL 块的执行部分是必选的。注意 END 关键字后面用分号结尾。
- 异常处理部分:该部分是可选的,该部分用 EXCEPTION 关键字把可执行部分分成两个小部分,之前的程序是正常运行的程序,一旦出现异常就跳转到异常部分执行。
PL/SQL 是一种编程语言,与 Java 和 C#一样,除了有自身独有的数据类型、变量声明和赋值以及流程控制语句外,PL/SQL 还有自身的语言特性:
PL/SQL 对大小写不敏感,为了良好的程序风格,开发团队都会选择一个合适的编码标准。比如有的团队规定:关键字全部大些,其余的部分小写。
PL/SQL 块中的每一条语句都必须以分号结束,SQL 语句可以是多行的,但分号表示该语句结束。一行中可以有多条 SQL 语句,他们之间以分号分隔,但是不推荐一行中写多条语句。
PL/SQL 中的特殊符号说明:
类型 | 符号 | 说明 |
赋值运算符 | := | Java 和 C#中都是等号,PL/SQL 的赋值是:= |
特殊字符 | || | 字符串连接操作符。 |
- | PL/SQL 中的单行注释。 | |
/*,*/ | PL/SQL 中的多行注释,多行注释不能嵌套。 | |
<<,>> | 标签分隔符。只为了标识程序特殊位置。 | |
.. | 范围操作符,比如:1..5 标识从1到5 | |
算术运算符 | +,-,*,/ | 基本算术运算符。 |
** | 求幂操作,比如:3**2=9 | |
关系运算符 | >,<,>=,<=,= | 基本关系运算符,=表示相等关系,不是赋值。 |
<>,!= | 不等关系。 | |
逻辑运算符 | AND,OR,NOT | AND,OR,NOT |
(1)变量声明
PL/SQL 支持 SQL 中的数据类型,PL/SQL 中正常支持 NUMBER,VARCHAR2,DATE 等 Oracle SQL 数据类型。声明变量必须指明变量的数据类型,也可以声明变量时对变量初始化,变量声明必须在声明部分。声明变量的语法是:
语法格式:声明变量
变量名 数据类型[ :=初始值]
语法解析:
数据类型如果需要长度,可以用括号指明长度,比如:varchar2(20)。
代码演示:声明变量
代码解析:
- 声明一个变量 sname,初始化值是“jerry”。字符串用单引号,如果字符串中出现单引号可以使用两个单引号(’’)来表示,即单引号同时也具有转义的作用。
- 对变量 sname 重新赋值,赋值运算符是“:=”。
- dbms_output.put_line 是输出语句,可以把一个变量的值输出,在 SQL*Plus 中输出数据时,可能没有结果显示,可以使用命令:set serveroutput on 设置输出到 SQL*Plus控制台上。
对变量赋值还可以使用 SELECT…INTO 语句从数据库中查询数据对变量进行赋值。但是查询的结果只能是一行记录,不能是零行或者多行记录。
代码演示:变量赋值
代码解析:
- 变量初始化时,可以使用 DEFAULT 关键字对变量进行初始化。
- 使用 select…into 语句对变量 sname 赋值,要求查询的结果必须是一行,不能是多行或者没有记录。
(2)声明常量
常量在声明时赋予初值,并且在运行时不允许重新赋值。使用 CONSTANT 关键字声明常量。
代码演示:声明常量
代码解析:
- 声明常量时使用关键字 CONSTANT,常量初值可以使用赋值运算符(:=)赋值,也可以使用 DEFAULT 关键字赋值。
在 SQL*Plus 中还可以声明 Session(会话,也就是一个客户端从连接到退出的过程称为当前用户的会话。)全局级变量,该变量在整个会话过程中均起作用,类似的这种变量称为宿主变量。宿主变量在 PL/SQL 引用时要用“:变量名”引用。
代码演示:宿主常量
代码解析:
- 可以使用 var 声明宿主变量。
- PL/SQL 中访问宿主变量时要在变量前加“:”。
- 在 SQL*Plus 中,使用 print 可以输出变量中的结果。
3. PL/SQL 数据类型
PL/SQL 的数据类型包括标量数据类型,引用数据类型和存储文本、图像、视频、声音等非结构化的大数据类型(LOB 数据类型)等。
(1) 标量数据类型
标量数据类型的变量只有一个值,且内部没有分量。标量数据类型包括数字型,字符型,日期型和布尔型。这些类型有的是 Oracle SQL 中定义的数据类型,有的是 PL/SQL 自身附加的数据类型。字符型和数字型又有子类型,子类型只与限定的范围有关,比如 NUMBER 类型可以表示整数,也可以表示小数,而其子类型 POSITIVE 只表示正整数。
类型 | 说明 |
VARCHAR2(长度) | 可变长度字符串,Oracle SQL 定义的数据类型,在 PL/SQL 中使用时最常 32767 字节。在 PL/SQL 中使用没有默认长度,因此必须指定。 |
NUMBER(精度,小数) | Oracle SQL 定义的数据类型 |
DATE | Oracle SQL 定义的日期类型 |
TIMESTAMP | Oracle SQL 定义的日期类型 |
CHAR(长度) | Oracle SQL 定义的日期类型,固定长度字符,最长 32767 字节,默认长度是 1,如果内容不够用空格代替。 |
LONG | Oracle SQL 定义的数据类型,变长字符串基本类型,最长 32760 字节。在 Oracle SQL 中最长 2147483647 字节。 |
BOOLEAN | PL/SQL 附加的数据类型,逻辑值为 TRUE、FALSE、NULL |
BINARY_INTEGER | PL/SQL 附加的数据类型,介于-2^31 和 2^31 之间的整数。 |
PLS_INTEGER | PL/SQL 附加的数据类型,介于 -2^31 和 2^31 之间的整数。类似于BINARY_INTEGER,只是 PLS_INTEGER 值上的运行速度更快。 |
NATURAL | PL/SQL 附加的数据类型,BINARY_INTEGER 子类型,表示从 0 开始的自然数。 |
NATURALN | 与 NATURAL 一样,只是要求 NATURALN 类型变量值不能为 NULL。 |
POSITIVE | PL/SQL 附加的数据类型,BINARY_INTEGER 子类型,正整数。 |
POSITIVEN | 与 POSITIVE 一样,只是要求 POSITIVE 的变量值不能为 NULL。 |
REAL | Oracle SQL 定义的数据类型,18 位精度的浮点数 |
INT,INTEGER,SMALLINT | Oracle SQL 定义的数据类型,NUMBERDE 的子类型,38 位精度整数。 |
SIGNTYPE | PL/SQL 附加的数据类型,BINARY_INTEGER 子类型。值有:1、-1、0。 |
STRING | 与 VARCHAR2 相同。 |
(2)属性数据类型
当声明一个变量的值是数据库中的一行或者是数据库中某列时,可以直接使用属性类型来声明。Oracle 中存在两种属性类型:%TYPE 和%ROWTYPE。
- % ROWTYPE
引用数据库表中的一行作为数据类型,即 RECORD 类型(记录类型),是 PL/SQL 附加的数据类型。表示一条记录,就相当于 C#中的一个对象。可以使用“.”来访问记录中的属性。
代码解析:
- 声明一个 myemp 对象,该对象表示 EMP 表中的一行。
- 从 EMP 表中查询一条记录放入 myemp 对象中。
- 访问该对象的属性可以使用“.”。
- %TYPE
引用某个变量或者数据库的列的类型作为某变量的数据类型。
代码演示:%TYPE 应用
代码解析:
- 定义变量 sal 为 emp 表中 sal 列的类型。
- 定义 totalsal 是变量 mysal 的类型。
%TYPE 可以引用表中的某列作的类型为变量的数据类型,也可以引用某变量的类型作为新变量的数据类型。
4. PL/SQL 条件控制和循环控制
PL/SQL 程序可通过条件或循环结构来控制命令执行的流程。PL/SQL 提供了丰富的流程控制语句,与 C#一样也有三种控制结构:
- 顺序结构
- 条件结构
- 循环结构
(1)条件控制
C#中的条件控制使用关键字 if 和 switch。PL/SQL 中关于条件控制的关键字有 IF-THEN、IF-THEN-ELSE、IF-THEN-ELSIF 和多分支条件 CASE。
- IF-THEN
该结构先判断一个条件是否为 TRUE,条件成立则执行对应的语句块,与 C#中的 if 语句很相似,具体语法是:
C#中 if 语法 | PL/SQL 中 IF 语法 |
if (条件){ //条件结构体 } |
IF 条件 THEN --条件结构体 END IF; |
说明:
- 用 IF 关键字开始,END IF 关键字结束,注意 END IF 后面有一个分号。
- 条件部分可以不使用括号,但是必须以关键字 THEN 来标识条件结束,如果条件成立,则执行 THEN 后到对应 END IF 之间的语句块内容。如果条件不成立,则不执行条件语句块的内容。
- C#结构用一对大括号来包含条件结构体的内容。PL/SQL 中关键字 THEN 到 END IF 之间的内容是条件结构体内容。
- 条件可以使用关系运算符合逻辑运算符。
案例 1:查询 JAMES 的工资,如果大于 900 元,则发奖金 800 元。
代码演示:IF-THEN 应用
SQL> declare
newSal emp.sal %TYPE;
begin
select sal into newSal from emp where ename='JAMES';
if newSal>900 then
update emp
set comm=800
where ename='JAMES';
end if;
commit;
end;
/
PL/SQL procedure successfully completed.
代码解析:
- 先判断条件,如果条件为 TRUE,则执行条件结构体内部的内容。
- 在 PL/SQL 块中可以使用事务控制语句,该 COMMIT 同时也能把 PL/SQL 块外没有提交的数据一并提交,使用时需要注意。
- IF-THEN-ELSE
语法格式:IF-THEN-ELSE
C#中 if 语法 | PL/SQL 中 IF 语法 |
if (条件){ //条件成立结构体 } else{ //条件不成立结构体 } |
IF 条件 THEN --条件成立结构体 ELSE --条件不成立结构体 END IF; |
语法解析:
把 ELSE 与 IF-THEN 连在一起使用,如果 IF 条件不成立则执行就会执行 ELSE 部分的语句。
案例 2:查询 JAMES 的工资,如果大于 900 元,则发奖金 800 元,否则发奖金 400 元。
代码演示:IF-THEN-ELSE 应用
SQL> declare
newSal emp.sal %TYPE;
begin
select sal into newSal from emp
where ename='JAMES';
if newSal>900 then
update emp
set comm=800
where ename='JAMES';
else
update emp
set comm=400
where ename='JAMES';
end if;
end;
/
PL/SQL procedure successfully completed.
- IF-THEN-ELSIF
语法格式:IF-THEN-ELSIF
C#中 if 语法 | PL/SQL 中 IF 语法 |
if (条件 2){ //条件成立结构体 } else if(条件 2){ //条件不成立结构体 } else{ //以上条件都不成立结构体 } |
IF 条件 1 THEN --条件 1 成立结构体 ELSIF 条件 2 THEN --条件 2 成立结构体 ELSE --以上条件都不成立结构体 END IF; |
语法解析:
PL/SQL 中的再次条件判断中使用关键字 ELSIF,而 C#使用 else if。
案例 3:查询 JAMES 的工资,如果大于 1500 元,则发放奖金 100 元,如果工作大于 900元,则发奖金 800 元,否则发奖金 400 元。
代码演示:IF-THEN-ELSIF 应用
SQL> declare
newSal emp.sal % TYPE;
begin
select sal into newSal from emp
where ename='JAMES';
if newSal>1500 then
update emp
set comm=1000
where ename='JAMES';
elsif newSal>1500 then
update emp
set comm=800
where ename='JAMES';
else
update emp
set comm=400
where ename='JAMES';
end if;
end;
/
PL/SQL procedure successfully completed.
- CASE
CASE 是一种选择结构的控制语句,可以根据条件从多个执行分支中选择相应的执行动作。也可以作为表达式使用,返回一个值。类似于 C#中的 switch 语句。语法是:
语法格式:CASE
CASE [selector]
WHEN 表达式 1 THEN 语句序列 1;
WHEN 表达式 2 THEN 语句序列 2;
WHEN 表达式 3 THEN 语句序列 3;
……
[ELSE 语句序列 N];
END CASE;
语法解析:
如果存在选择器 selector,选择器 selector 与 WHEN 后面的表达式匹配,匹配成功就执行 THEN 后面的语句。如果所有表达式都与 selector 不匹配,则执行 ELSE 后面的语句。
案例 4:输入一个字母 A、B、C 分别输出对应的级别信息。
代码演示:CASE 中存在 selector,不返回值
declare
v_grade char(1):=upper('&p_grade');
begin
case v_grade
when 'A' then
dbms_output.put_line('Excellent');
when 'B' then
dbms_output.put_line('Very Good');
when 'C' then
dbms_output.put_line('Good');
else
dbms_output.put_line('No such grade');
end case;
end;
代码解析:
- & grade 表示在运行时由键盘输入字符串到 grade 变量中。
- v_grade 分别于 WHEN 后面的值匹配,如果成功就执行 WHEN 后的程序序列。
CASE 语句还可以作为表达式使用,返回一个值。
代码演示:CASE 中存在 selector,作为表达式使用
declare
v_grade char(1):=upper('&grade');
p_grade varchar(20);
begin
p_grade :=
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||',the result is ' || p_grade);
end;
代码解析:
- CASE 语句可以返回一个结果给变量 p_grade
PL/SQL 还提供了搜索 CASE 语句。也就是说,不使用 CASE 中的选择器,直接在 WHEN后面判断条件,第一个条件为真时,执行对应 THEN 后面的语句序列。
代码演示:搜索 CASE
declare
v_grade char(1):=upper('&grade');
p_grade varchar(20);
begin
p_grade :=
case
when v_grade='A' then
'Excellent'
when v_grade='B' then
'Very Good'
when v_grade='C' then
'Good'
else
'No such grade'
end;
dbms_output.put_line('Grade:' ||v_grade||',the result is '||p_grade);
end;
(2)循环结构
PL/SQL提供了丰富的循环结构来重复执行一些列语句。 Oracle 提供的循环类型有:
- 无条件循环 LOOP -END LOOP 语句
- WHILE循环语句
- FOR循环语句
在上面的三类循环中 EXIT 用来强制结束循环,相当于 C#循环中的 break。
- LOOP 循环
LOOP 循环是最简单的循环,也称为无限循环,LOOP 和 END LOOP 是关键字。
语法格式:LOOP 循环
LOOP
--循环体
END LOOP;
语法格式:
- 循环体在 LOOP 和 END LOOP 之间,在每个 LOOP 循环体中,首先执行循环体中的语句序列,执行完后再重新开始执行。
- 在 LOOP 循环中可以使用 EXIT 或者[EXIT WHEN 条件]的形式终止循环。否则该循环就是死循环。
案例 5:执行 1+2+3+…+100 的值
declare
counter number(3):=0;
sumResult number:=0;
begin
loop
counter:=counter+1;
sumResult:=sumResult+counter;
if counter >= 100 then
exit;
end if;
end loop;
dbms_output.put_line('result is:'||to_char(sumResult));
end;
代码解析:
- LOOP 循环中可以使用 IF 结构嵌套 EXIT 关键字退出循环
- 注释行,该行可以代替①中的循环结构,WHEN 后面的条件成立时跳出循环。
- WHILE 循环
与 C#中的 while 循环很类似。先判断条件,条件成立再执行循环体。
语法格式:WHILE
C#中 while 语法 | PL/SQL 中 WHILE 语法 |
while (条件){ //循环体体 } |
WHILE 条件 LOOP --循环体 END LOOP; |
案例 6:WHILE 循环
代码演示:WHILE 循环
declare
counter number(3):=0;
sumResult number:=0;
begin
while counter<100 loop
counter := counter+1;
sumResult := sumResult+counter;
end loop;
dbms_output.put_line('result is:' || sumResult);
end;
- FOR 循环
FOR 循环需要预先确定的循环次数,可通过给循环变量指定下限和上限来确定循环运行的次数,然后循环变量在每次循环中递增(或者递减)。FOR 循环的语法是:
语法格式:FOR 循环
FOR 循环变量 IN [REVERSE] 循环下限..循环上限 LOOP LOOP
--循环体
END LOOP;
语法解析:
循环变量:该变量的值每次循环根据上下限的 REVERSE 关键字进行加 1 或者减 1。
REVERSE:指明循环从上限向下限依次循环。
案例 7:FOR 循环
代码演示:FOR 循环
declare
counter number(3):=0;
sumResult number:=0;
begin
for counter in 1..100 loop
sumResult:=sumResult+counter;
end loop;
dbms_output.put_line('result is:'||sumResult);
end;
(3)顺序结构
- 顺序结构
在程序顺序结构中有两个特殊的语句。GOTO 和 NULL
- GOTO 语句
GOTO 语句将无条件的跳转到标签指定的语句去执行。标签是用双尖括号括起来的标示符,在 PL/SQL 块中必须具有唯一的名称,标签后必须紧跟可执行语句或者 PL/SQL 块。GOTO 不能跳转到 IF 语句、CASE 语句、LOOP 语句、或者子块中。
- NULL 语句
NULL 语句什么都不做,只是将控制权转到下一行语句。NULL 语句是可执行语句。NULL语句在 IF 或者其他语句语法要求至少需要一条可执行语句,但又不需要具体操作的地方。比如 GOTO 的目标地方不需要执行任何语句时。
案例 8:GOGO 和 NULL
代码演示:GOTO 和 NULL
declare
sumsal emp.sal%type;
begin
select sum(sal) into sumsal from emp;
if sumsal>20000 then
goto first_label;
else
goto second_label;
end if;
<<first_label>>
dbms_output.put_line('above 20000:' || sumsal);
<<second_label>>
null;
end;
代码解析:
- 跳转到程序 first_label 位置,就是②的位置,first_label 是一个标签,用两个尖括号包含。
- 无条件跳转到 sedond_label 位置,就是④的位置。④处不执行任何内容,因此是一个 NULL 语句。
与 C#一样,在 PL/SQL 中,各种循环之间可以相互嵌套。
5. PL/SQL 中劢态执行 SQL 语句
在 PL/SQL 程序开发中,可以使用 DML 语句和事务控制语句,但是还有很多语句(比如DDL 语句)不能直接在 PL/SQL 中执行。这些语句可以使用动态 SQL 来实现。
PL/SQL 块先编译然后再执行,动态 SQL 语句在编译时不能确定,只有在程序执行时把SQL 语句作为字符串的形式由动态 SQL 命令来执行。在编译阶段 SQL 语句作为字符串存在,程序不会对字符串中的内容进行编译,在运行阶段再对字符串中的 SQL 语句进行编译和执行,动态 SQL 的语法是:
语法格式:动态 SQL
EXECUTE IMMEDIATE 动态语句字符串
[INTO 变量列表]
[USING 参数列表]
语法解析:
如果动态语句是 SELECT 语句,可以把查询的结果保存到 INTO 后面的变量中。如果动态语句中存在参数,USING 为语句中的参数传值。
动态 SQL 中的参数格式是:[:参数名],参数在运行时需要使用 USING 传值。
案例 9:动态 SQL
代码演示:动态 SQL
declare
sql_stmt varchar2(200); --动态sql
emp_id number(4):=7566;
salary number(7,2);
dept_id number(2):=90;
dept_name varchar2(14):='personnel';
location varchar2(13):='dallas';
emp_rec emp%rowtype;
begin
--无子句execute immediate
execute immediate 'create table bonusl (id number,amt number)';
-- using子句execute immediate
sql_stmt:='insert into dept values(:1,:2,:3)';
execute immediate sql_stmt using dept_id,dept_name,location;
--into子句的execute immediate
sql_stmt:='select * from emp where empno=:id';
execute immediate sql_stmt into emp_rec using emp_id;
--returning into 子句的execute immediate
sql_stmt:='update emp set sal=2000 where empno=:1
returning sal into :2';
execute immediate sql_stmt using emp_id returning into salary;
execute immediate 'delete from dept where deptno=:num'
using dept_id;
end;
代码解析:
- 动态执行一个完整的 SQL 语句。
- SQL 语句中存在 3 个参数分别标识为:[:1、:2、:3],因此需要用 USING 关键字对三个参数分别赋值。
- 对动态查询语句可以使用 INTO 子句把查询的结果保存到一个变量中,要求该结果只能是单行。
- 在 Oracle 的 insert,update,delete 语句都可以使用 RETURNING 子句把操作影响的行中的数据返回,对 SQL 语句中存在 RETURNING 子句时,在动态执行时可以使用RETURNING INTO 来接收。
- 动态执行参数中可以是:[:数字]也可以是[:字符串]。
6. PL/SQL 的异常处理
在程序运行时出现的错误,称为异常。发生异常后,语句将停止执行,PL/SQL 引擎立即将控制权转到 PL/SQL 块的异常处理部分。异常处理机制简化了代码中的错误检测。PL/SQL中任何异常出现时,每一个异常都对应一个异常码和异常信息。比如:
(1)预定义异常
为了 Oracle 开发和维护的方便,在 Oracle 异常中,为常见的异常码定义了对应的异常名称,称为预定义异常,常见的预定义异常有:
异常名称 | 异常码 | 描述 |
DUP_VAL_ON_INDEX | ORA-00001 | 试图向唯一索引列插入重复值 |
INVALID_CURSOR | ORA-01001 | 试图进行非法游标操作。 |
INVALID_NUMBER | ORA-01722 | 试图将字符串转换为数字 |
NO_DATA_FOUND | ORA-01403 | SELECT INTO 语句中没有返回任何记录。 |
TOO_MANY_ROWS | ORA-01422 | SELECT INTO 语句中返回多于 1 条记录。 |
ZERO_DIVIDE | ORA-01476 | 试图用 0 作为除数。 |
CURSOR_ALREADY_OPEN | ORA-06511 | 试图打开一个已经打开的游标 |
PL/SQL 中用 EXCEPTION 关键字开始异常处理。具体语法是:
语法格式:异常处理
BEGIN
--可执行部分
EXCEPTION -- 异常处理开始
WHEN 异常名 1 THEN
--对应异常处理
WHEN 异常名 2 THEN
--对应异常处理
……
WHEN OTHERS THEN
--其他异常处理
END;
语法解析:
异常发生时,进入异常处理部分,具体的异常与若干个 WHEN 子句中指明的异常名匹配,匹配成功就进入对应的异常处理部分,如果对应不成功,则进入 OTHERS 进行处理。
案例 10:异常处理
declare
newSal emp.sal%type;
begin
select sal into newSal from emp;
exception
when too_many_rows then
dbms_output.put_line('rows too much!!!');
when others then
dbms_output.put_line('unknow exception!!');
end;
/
(2)自定义异常
除了预定义异常外,用户还可以在开发中自定义异常,自定义异常可以让用户采用与PL/SQL 引擎处理错误相同的方式进行处理,用户自定义异常的两个关键点:
- 异常定义:在 PL/SQL 块的声明部分采用 EXCEPTION 关键字声明异常,定义方法与定义变量相同。比如声明一个 myexception 异常方法是:myexception EXCEPTION;
- 异常引发:在程序可执行区域,使用 RAISE 关键字进行引发。比如引发 myexception方法是:RAISE myexception;
案例 11:自定义异常
代码演示:自定义异常
declare
sal emp.sal%type;
myexp exception;
begin
select sal into sal from emp where ename='JAMES';
if sal<5000 then
raise myexp;
end if;
exception
when no_data_found then
dbms_output.put_line('No recordset find!');
when myexp then
dbms_output.put_line('sal is to less!');
end;
/
代码解析:
- 用 EXCEPTION 定义一个异常变量 myexp
- 在一定条件下用 RAISE 引发异常 myexp
- 在异常处理部分,捕获异常,如果不处理异常,该异常就抛给程序执行者。
(3) 引发应用程序异常
在 Oracle 开发中,遇到的系统异常都有对应的异常码,在应用系统开发中,用户自定义的异常也可以指定一个异常码和异常信息,Oracle 系统为用户预留了自定义异常码,其范围介于-20000 到-20999 之间的负整数。引发应用程序异常的语法是:
RAISE_APPLICATION_ERROR(异常码,异常信息)
案例 12:引发系统异常
代码演示:引发应用系统异常
代码解析:
- 引发应用系统异常,指明异常码和异常信息。
- 在控制台上显示异常码和异常信息。
如果要处理未命名的内部异常,必须使用 OTHERS 异常处理器。也可以利用 PRAGMA EXCEPTION_INIT 把一个异常码与异常名绑定。
PRAGMA 由编译器控制,PRAGMA 在编译时处理,而不是在运行时处理。EXCEPTION_INIT告诉编译器将异常名与 ORACLE 错误码绑定起来,这样可以通过异常名引用任意的内部异常,并且可以通过异常名为异常编写适当的异常处理器。PRAGMA EXCEPTION_INIT 的语法是:PRAGMA EXCEPTION_INIT(异常名,异常码)
这里的异常码可以是用户自定义的异常码,也可以是 Oracle 系统的异常码。
案例 13:PRAGMA EXCEPTION_INIT 异常
代码演示:PRAGMA EXCEPTION_INIT 异常
<<outterseg>>
DECLARE
null_salary EXCEPTION;
PRAGMA EXCEPTION_INIT(null_salary, -20101);
BEGIN
<<innerStart>>
DECLARE
curr_comm NUMBER;
BEGIN
SELECT comm INTO curr_comm FROM emp WHERE empno = &empno;
IF curr_comm IS NULL THEN
RAISE_APPLICATION_ERROR(-20101, 'Salary is missing');
ELSE
dbms_output.put_line('有津贴');
END IF;
END;
EXCEPTION
WHEN NO_DATA_FOUND THEN
dbms_output.put_line('没有发现行');
WHEN null_salary THEN
dbms_output.put_line('津贴未知');
WHEN OTHERS THEN
dbms_output.put_line('未知异常');
END;
/
代码解析:
- 把异常名称 null_salary 与异常码-20101 关联,该语句由于是预编译语句,必须放在声明部分。也就是说-20101 的异常名称就是 null_salary。
- 嵌套 PL/SQL 语句块
- 在内部 PL/SQL 语句块中引发应用系统异常-20101。
- 在外部的 PL/SQL 语句块中就可以用异常名 null_salary 进行捕获。