oracle第15章PLSQL程序设计.ppt
1,第15章 PL/SQL程序设计,2,本章内容,PL/SQL概述PL/SQL基础控制结构游标异常处理存储子程序包触发器,3,本章要求,掌握PL/SQL程序设计基础知识掌握存储过程、函数、包、触发器的应用,4,15.1 PL/SQL概述,PL/SQL特点PL/SQL功能特性PL/SQL执行过程与开发工具,5,特点,与SQL语言紧密集成。减小网络流量,提高应用程序的运行性能。模块化的程序设计功能,提高了系统可靠性。服务器端程序设计,可移植性好。,6,功能特性,语句块结构异常处理变量和类型条件语句循环结构游标过程、函数和触发器包集合动态SQL对象特性,7,执行过程与开发工具,PL/SQL块,SQL语句,客户端应用程序,PL/SQL引擎,数据库服务器,过程化语句执行器,SQL执行器,块中SQL语句,PL/SQL执行过程,8,PL/SQL开发工具SQL*PLUSProcedure BuilderOracle Form、Oracle ReportsPL/SQL Developer,9,15.2 PL/SQL基础,PL/SQL程序结构 词法单元 数据类型变量与常量编译指示PL/SQL中的SQL语句,10,PL/SQL程序结构,PL/SQL块的组成PL/SQL块分类,11,PL/SQL块的组成PL/SQL语言以块为单位,块中可以嵌套子块。一个基本的PL/SQL块由3部分组成:声明(DECLARE),可执行部分(BEGIN),异常处理部分EXCEPTION)。,12,声明部分声明部分以关键字DECLARE开始,BEGIN结束。主要用于声明变量、常量、数据类型、游标、异常处理名称以及本地(局部)子程序定义等。可执行部分执行部分是PL/SQL块的功能实现部分,以关键字BEGIN开始,EXCEPTION或END结束(如果PL/SQL块中没有异常处理部分,则以END结束)。该部分通过变量赋值、流程控制、数据查询、数据操纵、数据定义、事务控制、游标处理等实现块的功能。异常处理部分异常处理部分以关键字EXCEPTION开始,END结束。该部分用于处理该块执行过程中产生的异常。,13,注意:执行部分是必需的,而声明部分和异常部分是可选的;可以在一个块的执行部分或异常处理部分嵌套其他的PL/SQL块;所有的PL/SQL块都是以“END;”结束,14,PL/SQL块分类匿名块命名块函数存储过程包触发器,15,词法单元,字符集标识符分隔符常量值注释,16,字符集大小写字母:AZ,az数字:09空白:制表符、空格和回车数字符号:+-*/=标点符号:!#$%&*()_|?;:,.“,17,标识符标识符以字母开头,后边可以跟字母、数字、货币符号、下划线和”#”标识符的最大长度为30字符,并且所有字符都是有效的。合法:X v_studentID TempVar非法:X+y _temp,18,+-*/=:=,!=()/*/,%;:.“.|=,*-,分隔符,19,常量值字符型常量数字型常量 布尔型常量:TURE、FALSE、NULL日期型常量,20,数据类型,数字类型字符类型日期/区间类型行标识类型布尔类型原始类型LOB类型记录类型集合类型,21,PL/SQL中常用的基本数据类型,22,记录类型的定义TYPE record_type IS RECORD(field1 datatype1 NOT NULLDEFAULT|:=expr1,field2 datatype2 NOT NULL DEFAULT|:=expr2,fieldn datatypen NOT NULL DEFAULT|:=exprn);,23,变量与常量,变量与常量的定义变量的作用域,24,变量声明,变量与常量的定义,变量定义的一般格式:CONSTANT NOT NULL DEFAULT|:=;说明每行只能定义一个标识符。如果加上关键字CONSTANT,则表示所定义的标识符为一个常量,必须为它赋初值。如果定义的标识符不能为空,则必须加上关键字NOT NULL,并赋初值。为标识符赋值时,使用赋值符号:=,默认值为空。,25,DECLARE v1 NUMBER(4);v2 NUMBER(4)NOT NULL:=10;v3 CONSTANT NUMBER(4)DEFAULT 100;BEGIN IF v1 IS NULL THEN DBMS_OUTPUT.PUT_LINE(V1 IS NULL!);END IF;DBMS_OUTPUT.PUT_LINE(v2|v3);END;,26,声明一个变量,使它的类型与某个变量或数据库基本表中某个列的数据类型一致,可以使用%TYPE。示例v_empno1 emp.empno%TYPE;v_empno2 v_empno1%TYPE;,27,变量的作用域,变量的作用域是指变量的有效作用范围,从变量声明开始,直到块结束。如果PL/SQL块相互嵌套,则在内部块中声明的变量是局部的,只能在内部块中引用,而在外部块中声明的变量是全局的,既可以在外部块中引用,也可以在内部块中引用。如果内部块与外部块中定义了同名变量,则在内部块中引用外部块的全局变量时需要使用外部块名进行标识。,28,DECLARE v_ename CHAR(15);v_outer NUMBER(5);BEGIN v_outer:=10;DECLARE v_ename CHAR(20);v_inner DATE;BEGIN v_inner:=sysdate;v_ename:=INNER V_ENAME;OUTER.v_ename:=OUTER V_ENAME;END;DBMS_OUTPUT.PUT_LINE(v_ename);END;,29,编译指示,编译指示是对编译程序发出的特殊指令,也称伪指令。关键字:PRAGMAPL/SQL提供以下四种编译指示:EXCEPTION_INIT告诉编译程序将一个特定的错误号与程序中所声明的异常标识符关联起来。RESTRICT_REFERENCES告诉编译程序打包程序的纯度,即对函数中可以使用的SQL语句和包变量进行限制。,30,SERIALLY_REUSEABLE告诉PL/SQL运行时引擎,在数据引用之间不要保持包级数据。AUTONOMOUS_TRANSACTION告诉编译程序,该程序块为自治事务,即该事务的提交和回滚是独立进行的。,31,中SQL语句,可以在PL/SQL中执行的SQL语句包括SELECT DML(UPDATE、DELETE、INSERT)事务控制语句(COMMIT、ROLLBACK、SAVEPOINT)注意DDL语句不可以直接使用,32,SELECTINTOSELECTINTO语句只能查询一个记录的信息,如果没有查询到任何数据,则会产生NO_DATA_FOUND异常;如果查询到多个记录,则会产生TOO_MANY_ROW异常。INTO句子后的变量用于接收查询的结果,变量的个数、顺序应该与查询的目标数据相匹配,也可以是记录类型的变量。DML语句,33,DECLAREv_emp emp%ROWTYPE;v_ename emp.ename%type;v_sal emp.sal%type;BEGINSELECT*INTO v_emp FROM emp WHERE ename=SMITH;DBMS_OUTPUT.PUT_LINE(v_emp.empno|v_emp.sal);select ename,sal INTO v_ename,v_sal FROM emp WHERE empno=7900;DBMS_OUTPUT.PUT_LINE(v_ename|v_sal);END;,34,DML语句PL/SQL中DML语句对标准SQL语句中的DML语句进行了扩展,允许使用变量。示例DECLARE v_empno emp.empno%TYPE:=7500;BEGIN INSERT INTO emp(empno,ename,sal,deptno)VALUES(v_empno,JOAN,2300,20);UPDATE emp SET sal=sal+100 WHERE empno=v_empno;DELETE FROM emp WHERE empno=v_empno;END;,35,WHERE标识符的区分系统首先查看WHERE子句中的标识符是否与表中的列名相同,如果相同,则该标识符被解释为列名;如果没有同名列,系统检查该标识符是不是PL/SQL语句块的变量。字符串比较填充比较:通过在短字符串后添加空格,使两个字符串达到相同长度,然后根据每个字符的ASCII码进行比较。非填充比较:根据每个字符的ASCII码进行比较,最先结束的字符串为小。PL/SQL中规定,对定长的字符串(CHAR类型的字符串和字符串常量)采用填充比较;如果比较的字符串中有一个是变长字符串(VARCHAR2类型的字符串),则采用非填充比较。,36,RETURNING如果要查询当前DML语句操作的记录的信息,可以在DML语句末尾使用RETURNING语句返回该记录的信息。RETURNING语句的基本语法:RETURNING select_list_item INTO variable_list|record_variable;,37,DECLARE v_sal emp.sal%TYPE;BEGIN UPDATE emp SET sal=sal+100 WHERE empno=7844 RETURNING sal INTO v_sal;DBMS_OUTPUT.PUT_LINE(v_sal);END;,38,15.3 控制结构,选择结构循环结构跳转结构,39,选择结构,IF语句IF condition1 THEN statements1;ELSIF condition2 THEN statements2;ELSE else_statements;END IF;注意条件是一个布尔型变量或表达式,取值只能是TRUE,FALSE,NULL。,40,例如,输入一个员工号,修改该员工的工资,如果该员工为10号部门,工资增加100;若为20号部门,工资增加150;若为30号部门,工资增加200;否则增加300。,41,DECLAREv_deptno emp.deptno%type;v_increment NUMBER(4);v_empno emp.empno%type;BEGIN v_empno:=,42,搜索式CASE语句,基本语法CASE WHEN condition1 THEN statements1;WHEN condition2 THEN statements2;WHEN conditionn THEN statementsn;ELSE else_statements;END CASE;,43,等值比较的CASE语句,基本语法CASE test_value WHEN value1 THEN statements1;WHEN value2 THEN statements2;WHEN valuen THEN statementsn;ELSE else_ statements;END CASE;,44,DECLARE v_deptno emp.deptno%type;v_increment NUMBER(4);v_empno emp.empno%type;BEGIN v_empno:=,45,根据输入的员工号,修改该员工工资。如果该员工工资低于1000,则工资增加200;如果工资在1000-2000之间,则增加150;如果工资在2000-3000之间,则增加100;否则增加50。,46,DECLAREv_sal emp.sal%type;v_increment NUMBER(4);v_empno emp.empno%type;BEGINv_empno:=,47,循环结构,简单循环WHILE循环FOR循环,48,简单循环,语法 LOOP sequence_of_statement;EXIT WHEN condition;END LOOP;注意:在循环体中一定要包含EXIT语句,否则程序进入死循环。,49,例如,执行CREATE TABLE temp_table(num_col NUMBER,info_col CHAR(10)语句创建temp_table表,然后利用循环向temp_table表中插入50条记录。程序为:DECLARE v_counter BINARY_INTEGER:=1;BEGIN LOOP INSERT INTO temp_table VALUES(v_Counter,Loop index);v_counter:=v_counter+1;EXIT WHEN v_counter 50;END LOOP;END;,50,WHILE循环,基本语法 WHILE condition LOOP sequence_of_statement;END LOOP;,51,例如,利用WHILE循环向temp_table表中插入50条记录。程序为:DECLARE v_counter BINARY_INTEGER:=1;BEGIN WHILE v_counter=50 LOOP INSERT INTO temp_table VALUES(v_counter,Loop index);v_counter:=v_counter+1;END LOOP;END;,52,FOR循环,基本语法 FOR loop_counter IN REVERSE low_bound.high_bound LOOP sequence_of_statement;END LOOP;注意:循环变量不需要显式定义,系统隐含地将它声明为BINARY_INTEGER变量;系统默认时,循环变量从下界往上界递增计数,如果使用REVERSE关键字,则表示循环变量从上界向下界递减计数;循环变量只能在循环体中使用,不能在循环体外使用。,53,例如,利用FOR循环向temp_table表中插入50条记录。程序为:BEGIN FOR v_counter IN 1.50 LOOP INSERT INTO temp_table VALUES(v_counter,Loop Index);END LOOP;END;,54,跳转结构,语法格式:标号GOTO 标号;说明:块内可以跳转,内层块可以跳到外层块,但外层块不能跳到内层。IF语句不能跳入。不能从循环体外跳入循环体内。不能从子程序外部跳到子程序中。由于goto语句的缺点,建议尽量少用甚至不用goto语句。,55,15.4 游标,游标的概念及类型 显式游标 隐式游标,56,DECLARE v_counter BINARY_INTEGER:=1;BEGIN INSERT INTO temp_table VALUES(v_counter,Loop index);v_counter:=v_Counter+1;IF v_counter=50 THEN GOTO LABEL;END IF;END;,57,15.4游标,游标的概念及类型 显式游标 隐式游标,58,游标的及类型,游标的概念游标(CURSOR)是Oracle系统在内存中开辟的一个工作区,在其中存放SELECT语句返回的查询结果。使用游标时,SELECT语句查询的结果可以是单条记录,多条记录,也可以是零条记录。游标工作区中,存在着一个指针(POINTER),在初始状态它指向查询结果的首记录。,59,游标的类型显式游标由用户定义、操作,用于处理返回多行数据的SELECT查询。隐式游标由系统自动进行操作,用于处理DML语句和返回单行数据的SELECT查询。,60,显式游标,显式游标的操作显式游标的属性参数化显式游标显式游标的检索利用游标更新或删除数据,61,显式游标的操作,步骤定义游标打开游标检索游标关闭游标,62,定义游标,语法格式CURSOR cursor_name IS select_statement;说明游标必须在PL/SQL块的声明部分进行定义;游标定义时可以引用PL/SQL变量,但变量必须在游标定义之前定义;定义游标时并没有生成数据,只是将定义信息保存到数据字典中;游标定义后,可以使用cursor_name%ROWTYPE定义游标类型变量。,63,打开游标,语法格式OPEN cursor_name;说明检查变量的值执行游标定义时对应的SELECT语句,将查询结果检索到工作区中。游标指针指向第一个元组一旦游标打开,就无法再次打开,除非先关闭如果游标定义中的变量值发生变化,则只能在下次打开游标时才起作用。,64,检索游标,语法格式FETCH cursor_name INTO variable_list|record_variable;说明在使用FETCH语句之前必须先打开游标对游标第一次使用FETCH语句时,游标指针指向第一条记录,因此操作的对象是第一条记录,使用后,游标指针指向下一条记录。游标指针只能向下移动,不能回退INTO子句中的变量个数、顺序、数据类型必须与工作区中每行记录的字段数、顺序以及数据类型一一对应。,65,关闭游标,语法格式 CLOSE cursor_name;说明游标所对应的内存工作区变为无效,释放与游标相关的系统资源。,66,根据输入的部门号查询某个部门的员工信息,部门号在程序运行时指定。,67,DECLARE v_deptno emp.deptno%TYPE;CURSOR c_emp IS SELECT*FROM emp WHERE deptno=v_deptno;v_emp c_emp%ROWTYPE;BEGIN v_deptno:=,68,显式游标的属性,%ISOPEN布尔型。如果游标已经打开,返回TRUE,否则为FALSE。%FOUND布尔型,如果最近一次使用FETCH语句,有返回结果则为TRUE,否则为FALSE;%NOTFOUND布尔型,如果最近一次使用FETCH语句,没有返回结果则为TRUE,否则为FALSE;%ROWCOUNT数值型,返回到目前为止从游标缓冲区检索的元组数。,69,参数化显式游标,参数化游标定义语法格式:CURSOR cursor_name(parameter1 datatype,parameter2 datatype)IS select_statement 打开参数化游标的方法OPEN cursor_name(parameter1,parameter2),70,注意:定义游标时,只能指定参数的类型,而不能指定参数的长度、精度、刻度;打开带参数的游标时,实参的个数和数据类型等必须与游标定义时形参个数和数据类型等相匹配。,71,DECLARE CURSOR c_emp(p_deptno emp.deptno%TYPE)IS SELECT*FROM emp WHERE deptno=p_deptno;v_emp c_emp%ROWTYPE;BEGIN OPEN c_emp(10);LOOP FETCH c_emp INTO v_emp;EXIT WHEN c_emp%NOTFOUND;DBMS_OUTPUT.PUT_LINE(v_emp.empno|v_emp.ename);END LOOP;CLOSE c_emp;OPEN c_emp(20);LOOP FETCH c_emp INTO v_emp;EXIT WHEN c_emp%NOTFOUND;DBMS_OUTPUT.PUT_LINE(v_emp.empno|v_emp.ename);END LOOP;CLOSE c_emp;END;,72,显式游标的检索,利用简单循环检索游标利用WHILE循环检索游标利用FOR循环检索游标,73,利用简单循环检索游标,DECLARE CURSOR cursor_name IS SELECT;BEGIN OPEN cursor_name;LOOP FETCHINTO;EXIT WHEN cursor_name%NOTFOUND;END LOOP;CLOSE cursor_name;END;,74,利用简单循环统计并输出各个部门的平均工资。DECLARE CURSOR c_dept_stat IS SELECT deptno,avg(sal)avgsal FROM emp GROUP BY deptno;v_dept c_dept_stat%ROWTYPE;BEGINOPEN c_dept_stat;LOOP FETCH c_dept_stat INTO v_dept;EXIT WHEN c_dept_stat%NOTFOUND;DBMS_OUTPUT.PUT_LINE(v_dept.deptno|v_dept.avgsal);END LOOP;CLOSE c_dept_stat;END;,75,利用WHILE循环检索游标,DECLARE CURSOR cursor_name IS SELECT;BEGIN OPEN cursor_name;FETCHINTO;WHILE cursor_name%FOUND LOOP FETCHINTO;END LOOP;CLOSE cursor;END;,76,利用WHILE循环统计并输出各个部门的平均工资。DECLARE CURSOR c_dept_stat IS SELECT deptno,avg(sal)avgsal FROM emp GROUP BY deptno;v_dept c_dept_stat%ROWTYPE;BEGIN OPEN c_dept_stat;FETCH c_dept_stat INTO v_dept;WHILE c_dept_stat%FOUND LOOP DBMS_OUTPUT.PUT_LINE(v_dept.deptno|v_dept.avgsal);FETCH c_dept_stat INTO v_dept;END LOOP;CLOSE c_dept_stat;END;,77,利用FOR循环检索游标,DECLARE CURSOR cursor_name IS SELECT;BEGIN FOR loop_variable IN cursor_name LOOP END LOOP;END;,78,FOR循环说明,系统隐含地定义了一个数据类型为%ROWTYPE的变量,并以此作为循环的计算器。系统自动打开游标,不用显式地使用OPEN语句打开;系统重复地自动从游标工作区中提取数据并放入计数器变量中。系统自动进行%FOUND属性检查以确定是否有数据当游标工作区中所有的记录都被提取完毕或循环中断时,系统自动地关闭游标。,79,利用FOR循环统计并输出各个部门的平均工资。DECLARE CURSOR c_dept_stat IS SELECT deptno,avg(sal)avgsal FROM emp GROUP BY deptno;BEGIN FOR v_dept IN c_dept_stat LOOP DBMS_OUTPUT.PUT_LINE(v_dept.deptno|v_dept.avgsal);END LOOP;END;,80,隐式FOR游标BEGINFOR v_emp IN(select*from emp where deptno=10)LOOP DBMS_OUTPUT.PUT_LINE(v_emp.empno|v_emp.ename);END LOOP;END;,81,利用游标更新或删除数据,游标定义语法 CURSOR cursor_name IS SELECT select_list_item FROM table FOR UPDATE 更新或修改数据的语法为:UPDATE|DELETE WHERE CURRENT OF cursor_name 注意 由于COMMIT语句会释放会话拥有的任何锁,因此如果在检索游标的循环内使用COMMIT语句会释放定义游标时对数据加的锁,从而导致利用游标修改或删除数据的操作失败。,82,修改员工的工资,如果员工的部门号为10,工资提高100;部门号为20,工资提高150;部门号为30,工资提高200;否则工资提高250。,83,DECLARE CURSOR c_emp IS SELECT*FROM emp FOR UPDATE;v_increment NUMBER;BEGIN FOR v_emp IN c_emp LOOP CASE v_emp.deptno WHEN 10 THEN v_increment:=100;WHEN 20 THEN v_increment:=150;WHEN 30 THEN v_increment:=200;ELSE v_increment:=250;END CASE;UPDATE emp SET sal=sal+v_increment WHERE CURRENT OF c_emp;END LOOP;END;,84,隐式游标,用于处理INSERT、UPDATE、DELETE和SELECTINTO语句没有OPEN、FETCH、CLOSE命令属性 SQL%ISOPENSQL%FOUNDSQL%NOTFOUND SQL%ROWCOUNT,85,修改员工号为1000的员工工资,将其工资增加100。如果该员工不存在,则向emp表中插入一个员工号为1000,工资为1500的员工。BEGINUPDATE emp SET sal=sal+100 WHERE empno=1000;IF SQL%NOTFOUND THEN INSERT INTO emp(empno,sal)VALUES(1000,1500);END IF;END;或BEGINUPDATE emp SET sal=sal+100 WHERE empno=1000;IF SQL%ROWCOUNT=0 THENINSERT INTO emp(empno,sal)VALUES(1000,1500);END IF;END;,86,15.5 异常处理,异常概述异常处理过程 异常的传播,87,异常概述,Oracle错误处理机制异常的类型,88,Oracle错误处理机制概念一个错误对应一个异常,当错误产生时抛出相应的异常,并被异常处理器捕获,程序控制权传递给异常处理器,由异常处理器来处理运行时错误。,89,异常的类型预定义的Oracle异常非预定义的Oracle异常用户定义的异常,90,预定义的异常,91,92,93,非预定义异常,在语句块的声明部分声明一个异常名称e_integrity EXCEPTION;通过PRAGMA EXCEPTION-INIT 将异常与一个Oracle错误号相关联:PRAGMA EXCEPTION-INIT(e_integrity.-2291)在异常处理部分捕捉并处理异常:WHEN e_integrity THEN.,94,用户自定义的异常,用户自定义异常必须在声明部分进行声明。当异常发生时,系统不能自动触发,需要用户使用RAISE语句。在异常处理部分捕捉并处理异常。,95,异常处理过程,在声明部分为错误定义异常,包括非预定义异常和用户定义异常。e_exception EXCEPTION;PRAGMA EXCEPTION_INIT(e_exceptioin,-#);在执行过程中当错误产生时抛出与错误对应的异常。RAISE user_define_exception;在异常处理部分通过异常处理器捕获异常,并进行异常处理。,96,异常的捕获与处理,异常处理器的基本形式为EXCEPTIONWHEN exception1OR excetpion2THEN sequence_of_statements1;WHEN exceptioin3OR exception4THEN sequence_of_statements2;WHEN OTHERS THEN sequence_of_statementsn;END;,97,注意:一个异常处理器可以捕获多个异常,只需要在WHEN子句中用OR连接即可;一个异常只能被一个异常处理器捕获,并进行处理。,98,查询名为SMITH的员工工资,如果该员工不存在,则输出“There is not such an employee!”;如果存在多个同名的员工,则输出其员工号和工资。DECLARE v_sal emp.sal%type;BEGIN SELECT sal INTO v_sal FROM emp WHERE ename=SMITH;DBMS_OUTPUT.PUT_LINE(v_sal);EXCEPTION WHEN NO_DATA_FOUND THEN DBMS_OUTPUT.PUT_LINE(There is not such an emplyee!);WHEN TOO_MANY_ROWS THEN FOR v_emp IN(SELECT*FROM emp WHERE ename=SMITH)LOOP DBMS_OUTPUT.PUT_LINE(v_emp.empno|v_emp.sal);END LOOP;END;,99,删除dept表中部门号为10的部门信息,如果不能删除则输出“There are subrecords in emp table!”。DECLARE e_deptno_fk EXCEPTION;PRAGMA EXCEPTION_INIT(e_deptno_fk,-2292);BEGIN DELETE FROM dept WHERE deptno=10;EXCEPTION WHEN e_deptno_fk THEN DBMS_OUTPUT.PUT_LINE(There are subrecords in emp table!);END;,100,修改7844员工的工资,保证修改后工资不超过6000。DECLARE e_highlimit EXCEPTION;v_sal emp.sal%TYPE;BEGIN UPDATE emp SET sal=sal+100 WHERE empno=7844 RETURNING sal INTO v_sal;IF v_sal6000 THEN RAISE e_highlimit;END IF;EXCEPTION WHEN e_highlimit THEN DBMS_OUTPUT.PUT_LINE(The salary is too large!);ROLLBACK;END;,101,OTHERS异常处理器,OTHERS异常处理器是一个特殊的异常处理器,可以捕获所有的异常。通常,OTHERS异常处理器总是作为异常处理部分的最后一个异常处理器,负责处理那些没有被其他异常处理器捕获的异常。,102,DECLARE v_sal emp.sal%TYPE;e_highlimit EXCEPTION;BEGIN SELECT sal INTO v_sal FROM emp WHERE ename=JOAN;UPDATE emp SET sal=sal+100 WHERE empno=7900;IF v_sal6000 THEN RAISE e_highlimit;END IF;EXCEPTION WHEN e_highlimit THEN DBMS_OUTPUT.PUT_LINE(The salary is too large!);ROLLBACK;WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE(There is some wrong in selecting!);END;,103,可以通过两个函数来获取错误相关信息。SQLCODE:返回当前错误代码。如果是用户定义错误返回值为1;如果是ORA-1403:NO DATA FOUND错误,返回值为100;其他Oracle内部错误返回相应的错误号。SQLERRM:返回当前错误的消息文本。如果是Oracle内部错误,返回系统内部的错误描述;如果是用户定义错误,则返回信息文本为“User-defined Exception”。,104,DECLARE v_sal emp.sal%TYPE;e_highlimit EXCEPTION;v_code NUMBER(6);v_text VARCHAR2(200);BEGIN SELECT sal INTO v_sal FROM emp WHERE ename=JOAN;UPDATE emp SET sal=sal+100 WHERE empno=7900;IF v_sal6000 THEN RAISE e_highlimit;END IF;EXCEPTION WHEN e_highlimit THEN DBMS_OUTPUT.PUT_LINE(The salary is too large!);ROLLBACK;WHEN OTHERS THEN v_code:=SQLCODE;v_text:=SQLERRM;DBMS_OUTPUT.PUT_LINE(v_code|v_text);END;,105,异常的传播,可执行部分异常的传播如果当前语句块有该异常的处理器,则执行之,并且成功完成该语句块。然后,控制权传递到外层语句块。如果当前语句块没有该异常的处理器,则通过在外层语句块中产生该异常来传播该异常。然后,执行对外层语句块执行步骤1。如果没有外层语句块,则该异常将传播到调用环境。,106,DECLARE v_sal emp.sal%TYPE;BEGIN BEGIN SELECT sal INTO v_sal FROM emp WHERE ename=JOAN;EXCEPTION WHEN NO_DATA_FOUND THEN DBMS_OUTPUT.PUT_LINE(There is not such an employee!);END;DBMS_OUTPUT.PUT_LINE(Now this is outputted by outer block!);END;/There is not such an employee!Now this is outputted by outer block!,107,DECLARE v_sal emp.sal%TYPE;BEGIN BEGIN SELECT sal INTO v_sal FROM emp WHERE deptno=10;EXCEPTION WHEN NO_DATA_FOUND THEN DBMS_OUTPUT.PUT_LINE(There is not such an employee!);END;DBMS_OUTPUT.PUT_LINE(Now this is outputted by outer block!);EXCEPTION WHEN TOO_MANY_ROWS THEN DBMS_OUTPUT.PUT_LINE(There are more than one employee!);END;/There are more than one employee!,108,声明部分异常的传播声明部分的异常立刻传播到外层语句块,即使当前语句块有异常处理器。异常处理部分的异常的传播异常处理器中产生的异常,可以有RAISE语句显式产生,也可以通过运行时错误而隐含产生。异常立即被传播到外层语句块。,109,BEGIN DECLAREv_number NUMBER(6):=ABC;BEGIN v_number:=10;EXCEPTION WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE(This is outputted by inner block!);END;EXCEPTIONWHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE(This is outputted by outer block!);END;/This is outputted by outer block!,110,15.6 存储子程序,存储过程函数局部子程序,111,存储子程序是指被命名的PL/SQL块,以编译的形式存储在数据库服务器中,