oracle 隐士游标属性,隐式游标和ref游标总结

游标是构建在PL/SQL中,用来查询数据,获取记录集的指针。它让开发者 一次访问结果集中一行记录。

在oracle中提供了两种游标: 1 静态游标 2 ref游标

静态游标:静态游标是在编译的时候就被确定。然后把结果集复制到内存中 静态游标又分为两种:隐式游标和显示游标。

ref游标:ref游标是在运行的时候加载结果集

隐式游标 在PL/SQL中为所有的SQL数据操纵语句(包括返回一行的select)隐式声明游标

称为隐式游标。主要原因是用户不能直接命名和控制此类游标。当用户在PL/SQL

中使用数据操纵语句(DML)时,oracle预先定义一个名称为SQL的隐式游标,通过

检查隐式游标的属性获取与最近执行的SQL语句相关信息。 在执行DML语句之后,隐式游标属性返回信息。隐式游标属性包括: %found

%notfound  %rowcount

%isopen

1 %found

只有DML语句影响一行或多行时,%found属性才返回true

declare num number;

begin update emp set empno=123 where empno=111;

if sql%found then dbms_output.put_line('存在记录');

else dbms_output.put_line('不存在记录');

end if;

end;

2 %notfound

%notfound属性作用正好跟%found属性相反。如果DML语句没有影响任何行数

,则%notfound属性返回true.

declare

begin delete from emp where empno=111;

if sql%notfound then dbms_output.put_line('删除失败');

end if;

end;

3 %rowcount

%rowcount属性返回DML语句影响的行数。如果DML语句没有影响任何行数

,则%rowcount属性将返回0。

declare num number;

begin update emp set empno=123 where empno=111;

if sql%rowcount=0 then dbms_output.put_line('不存在记录');

else dbms_output.put_line('存在记录');

end if;

end;

4 %isopen

%isopen属性判断SQL游标是否已经打开。在执行SQL语句之后,oracle自动关闭SQL

游标,所以隐式游标的%isopen属性始终为false.

在PL/SQL中向标准的select语句增加单独的into子句,就可以将从表或视图中查询

记录赋予变量或行变量。需要注意的是select ..into 语句结果必须有且只能有一行。

如果查询没有返回行,PL/SQL将抛出no_data_found异常。如果查询返回多行,则抛出 too_many_rows

异常。如果抛出异常,则停止执行,控制权转移到异常处理部分(没有

异常处理,则程序中断)。在引发异常时,将不使用属性%found,%notfound,%rowcount来查明DML语句是否

已影响了行数。

declare num number;

begin select empno into num from emp where empno=111;

if sql%rowcount=0 or sql%notfound then

dbms_output.put_line('不存在记录');

else dbms_output.put_line('存在记录');

end if;

end;

显示游标 显示游标是由用户显示声明的游标。根据在游标中定义的查询,查询返回的行集合可以

包含零行或多行,这些行称为活动集。游标将指向活动集中的当前行。 显示游标的操作过程。使用显示游标的4个步骤:

(1)声明游标

(2)打开游标

(3)从游标中获取结果集

(4)关闭游标

cursor cursor_name [(parameter[,parameter])] [return

return_type] is select_statement; cursor_name 指游标的名称。

parameter  为游标指定输入参数。

return_type 定义游标提取行的行类型。

select_statement 为游标定义查询语句。

open 游标名称 fetch 从游标中提取行 close 关闭游标

1 打开游标,执行游标中定义的查询语句,绑定输入参数,将游标指针指 向结果集的BOF位置。 open

cursor_name [parameters]

2  fetch 在打开游标之后,可以从游标中提取记录 fetch

cursor_name into variable_name; fetch

是提取结果集中一行记录存储在变量中。每次提取之后,结果集指针 就向前移动一行。

3 close

在处理游标中的所有行之后,必须关闭游标,以释放分配给游标的所有资源。 close cursor_name

用户可以通过检查游标属性来确定游标的当前状态。

显示游标的属性如下:

%found:如果执行最后一条fetch语句,成功返回行,则%found属性为true。

%notfound:如果执行最后一条fetch语句,未能提取行,则%notfound属性为true。

%isopen:如果游标已经打开,则返回true,否则返回false。

%rowcount:返回到目前为止游标提取的行数。

%rowcount为数字类型属性。在第一

次获取之前,%rowcount为零。当fetch语句返回一行时,则该数加1。

declare info emp%rowtype;

cursor my_cur is select * from emp where empno=111;

begin open my_cur;

dbms_output.put_line(my_cur%rowcount); loop

if my_cur%isopen then

fetch my_cur into info;

exit when my_cur%notfound;

dbms_output.put_line(info.empno);

dbms_output.put_line(my_cur%rowcount);

end if; end loop;

close my_cur;

end;

使用显示游标删除或更新 使用游标时,如果处理过程中需要删除或更新。在定义游标查询语句时 必须使用select..for

update语句,而在执行delete或update时使用 where current of 子句指定游标当前行。

cursor cursor_name is select_statement for update[of column]

wait/nowait

在使用for update 子句声明游标之后,可以使用以下语法更新行

update table_name set column_name=column_value where current

of cursor_name;

update命令中使用的列必须出现在for update of 子句中 select

语句必须只包括一个表,而且delete和update语句只有在打开游标并且提取 特定行之后才能使用。

declare cursor cur_emp is select * from emp where sal<2000

for update of sal;

num emp%rowtype;

begin open cur_emp; loop

fetch cur_emp into num;

exit when cur_emp%notfound;

update emp set sal=2000 where current of cur_emp;

end loop;

close cur_emp;

end;

带参数的显示游标 PL/SQL中允许显示游标接受输入参数。用于声明带参数的显示游标语法

cursor cursor_name[ data_type] [return ] is select_statement

declare dept_num emp.deptno%type;

emp_num  emp.empno%type;

emp_nam  emp.ename%type;

cursor emp_cur(deptparam number) is select empno,ename from

emp where deptno=deptparam;

begin dept_num :=&部门编号;

open emp_cur(dept_num); loop

fetch emp_cur into emp_num,emp_nam;

exit when emp_cur%notfound;

dbms_output.put_line(emp_num||' '||emp_nam);

end loop;

close emp_cur;

end;

可以使用循环游标来简化显示游标  循环游标隐式打开显示游标(不需要open)

自动从结果集提取记录,然后处理完所有记录自动关闭游标。循环游标自动创建 %rowtype类型的变量并将此变量用做记录的索引。

循环游标语法如下:

for record_index in cursor_name record_

index是PL/SQL自动创建的变量,此变量的属性声明为%rowtype类型。作用 域for循环之内。 循环游标的特性有:

从游标中提取所有记录之后自动关闭游标。 提取和处理游标中每一条记录

提取记录之后%notfound属性为true则退出循环。如果未有结果集,则不进入循环。

declare cursor emp_cur is select * from emp;

begin for temp in emp_cur loop

dbms_output.put_line(temp.ename);

end loop;

end;

循环游标自动打开,提取,关闭。只适用于静态游标

ref游标 隐式游标和显示游标都是静态定义的。它们在编译的时候结果集就已经被确定。

如果想在运行的时候动态确定结果集,就要使用ref游标和游标变量。

创建ref游标需要两个步骤:

1 声明ref cursor类型

2 声明 ref cursor类型变量。

语法如下:

type ref_cursor_name is ref cursor [return record_type]

其中,return 用于指定游标提取结果集的返回类型。有return表示是强类型ref游标,

没有return表示是弱类型的游标。弱类型游标可以提取任何类型的结果集。

定义游标变量之后,就可以在PL/SQL执行部门打开游标变量 open cursor_name for

select_statement;

declare type emp_cur is ref cursor;

my_cur emp_cur;

num number;

selection varchar(2):='&请输入编号';

begin

if selection='1' then  dbms_output.put_line('员工信息');

open my_cur for select deptno from emp;

elsif selection='2' then  dbms_output.put_line('部门信息');

open my_cur for select deptno from dept;

else

dbms_output.put_line('请输入员工信息(1)或门部信息(2)');

end if;

fetch my_cur into num;

while my_cur%found  loop

dbms_output.put_line(num);

fetch my_cur into num;

end loop;

close my_cur;

end;

在PL/SQL中可以执行动态SQL语句,execute immediate 语句只能语句处理返回单行

或没有返回的SQL语句,ref游标则可以处理返回结果集的动态SQL。

ref游标的声明 方法与普通ref游标相同,只是在open时指定了动态SQL字符串。

open cursor_name for dynamic_select_string [using

bind_argument_list]

declare type sql_cur is ref cursor;

my_cur sql_cur;

emp_info emp%rowtype;

sql_string varchar2(100):='&请输入查询字符串';

begin open my_cur for sql_string; loop

fetch my_cur into emp_info;

exit when my_cur%notfound;

dbms_output.put_line(emp_info.ename);

end loop;

close my_cur;

end;

游标变量的特点:

(1)游标变量可以从不同的结果集中提取记录

(2)游标变量可以做为存储过程参数进行传递

(3)游标变量可以引用游标的所有属性

(4)游标变量可以进行赋值运算 使用游标变量也有一定的限制:

(1)for update 子句不能与游标变量一起使用

(2)不允许在程序包使用游标变量(可以声明游标类型)

(3)另一台服务器上的子过程不能接受游标变量参数

(4)不能将NULL值赋给游标变量。

(5)游标变量不能使用比较运算符。

(6)数据库中的列不能存储游标变量。

总结:

(1)游标是使用在PL/SQL中,是用来查询数据,获取结果集的指针

(2)游标类型包括隐式游标,显示游标和ref游标。

(3)游标属性包括%found,%notfound,%rowcount,%isopen

(4)PL/SQL自动定义隐式游标,以获取最近执行SQL语句信息。

(5)循环游标简化处理游标中所有行的查询。

(6)在声明ref游标时,不要将它与select 语句相关联。

动态SELECT语句和动态游标的用法

Oracle支持动态SELECT语句和动态游标,动态的方法大大扩展了程序设计的能力。

对于查询结果为一行的SELECT语句,可以用动态生成查询语句字符串的方法,在程序执行阶段临时地生成并执行,语法是:

execute immediate 查询语句字符串 into 变量1[,变量2...];

以下是一个动态生成SELECT语句的例子。

【训练1】  动态SELECT查询。

Sql代码

1.SET SERVEROUTPUT ON

2.  DECLARE

3.  str varchar2(100);

4.  v_ename varchar2(10);

5.  begin

6.  str:='select ename from scott.emp where

empno=7788';

7.  execute immediate str into v_ename;

8.  dbms_output.put_line(v_ename);

9.  END;

SET SERVEROUTPUT ON

DECLARE

str varchar2(100);

v_ename varchar2(10);

begin

str:='select ename from scott.emp where

empno=7788';

execute immediate str into v_ename;

dbms_output.put_line(v_ename);

END;

执行结果为:

Sql代码

1.SCOTT

2.  PL/SQL 过程已成功完成。

SCOTT

PL/SQL 过程已成功完成。

说明:SELECT...INTO...语句存放在STR字符串中,通过EXECUTE语句执行。

在变量声明部分定义的游标是静态的,不能在程序运行过程中修改。虽然可以通过参数传递来取得不同的数据,但还是有很大的局限性。通过采用动态游标,可以在程序运行阶段随时生成一个查询语句作为游标。要使用动态游标需要先定义一个游标类型,然后声明一个游标变量,游标对应的查询语句可以在程序的执行过程中动态地说明。

定义游标类型的语句如下:

TYPE 游标类型名 REF CURSOR;

声明游标变量的语句如下:

游标变量名 游标类型名;

在可执行部分可以如下形式打开一个动态游标:

OPEN 游标变量名 FOR 查询语句字符串;

【训练2】  按名字中包含的字母顺序分组显示雇员信息。

输入并运行以下程序:

Sql代码

1.declare

2. type cur_type is ref cursor;

3. cur cur_type;

4. rec scott.emp%rowtype;

5. str varchar2(50);

6. letter char:= 'A';

7.begin

8.  loop

9.  str:= 'select ename from emp where ename like

''%'||letter||'%''';

10.  open cur for str;

11.  dbms_output.put_line('包含字母'||letter||'的名字:');

12.  loop

13.  fetch cur into rec.ename;

14.  exit when cur%notfound;

15.  dbms_output.put_line(rec.ename);

16.end loop;

17.  exit when letter='Z';

18.  letter:=chr(ascii(letter)+1);

19. end loop;

20.end;

declare

type cur_type is ref cursor;

cur cur_type;

rec scott.emp%rowtype;

str varchar2(50);

letter char:= 'A';

begin

loop

str:=

'select ename from emp where ename like ''%'||letter||'%''';

open cur

for str;

dbms_output.put_line('包含字母'||letter||'的名字:');

loop

fetch cur

into rec.ename;

exit when

cur%notfound;

dbms_output.put_line(rec.ename);

end loop;

exit when letter='Z';

letter:=chr(ascii(letter)+1);

end loop;

end;

运行结果为:

Sql代码

1.包含字母A的名字:

2.ALLEN

3.WARD

4.MARTIN

5.BLAKE

6.CLARK

7.ADAMS

8.JAMES

9.包含字母B的名字:

10.BLAKE

11.包含字母C的名字:

12.CLARK

13.SCOTT

包含字母A的名字:

ALLEN

WARD

MARTIN

BLAKE

CLARK

ADAMS

JAMES

包含字母B的名字:

BLAKE

包含字母C的名字:

CLARK

SCOTT

说明:使用了二重循环,在外循环体中,动态生成游标的SELECT语句,然后打开。通过语句letter:=chr(ascii(letter)+1)可获得字母表中的下一个字母。

异常处理

错误处理

错误处理部分位于程序的可执行部分之后,是由WHEN语句引导的多个分支构成的。错误处理的语法如下:

EXCEPTION

WHEN 错误1[OR 错误2] THEN

语句序列1;

WHEN 错误3[OR 错误4] THEN

语句序列2;

WHEN OTHERS

语句序列n;

END;

其中:

错误是在标准包中由系统预定义的标准错误,或是由用户在程序的说明部分自定义的错误,参见下一节系统预定义的错误类型。

语句序列就是不同分支的错误处理部分。

凡是出现在WHEN后面的错误都是可以捕捉到的错误,其他未被捕捉到的错误,将在WHEN

OTHERS部分进行统一处理,OTHENS必须是EXCEPTION部分的最后一个错误处理分支。如要在该分支中进一步判断错误种类,可以通过使用预定义函数SQLCODE(

)和SQLERRM( )来获得系统错误号和错误信息。

如果在程序的子块中发生了错误,但子块没有错误处理部分,则错误会传递到主程序中。

下面是由于查询编号错误而引起系统预定义异常的例子。

【训练1】  查询编号为1234的雇员名字。

Sql代码

1.SET SERVEROUTPUT ON

2.DECLARE

3.v_name VARCHAR2(10);

4.BEGIN

5.  SELECT  ename

6.  INTO  v_name

7.  FROM  emp

8.  WHERE  empno = 1234;

9.DBMS_OUTPUT.PUT_LINE('该雇员名字为:'|| v_name);

10.EXCEPTION

11.  WHEN NO_DATA_FOUND

THEN

12.  DBMS_OUTPUT.PUT_LINE('编号错误,没有找到相应雇员!');

13.  WHEN OTHERS THEN

14.  DBMS_OUTPUT.PUT_LINE('发生其他错误!');

15.END;

SET SERVEROUTPUT ON

DECLARE

v_name VARCHAR2(10);

BEGIN

SELECT  ename

INTO  v_name

FROM  emp

WHERE  empno = 1234;

DBMS_OUTPUT.PUT_LINE('该雇员名字为:'|| v_name);

EXCEPTION

WHEN NO_DATA_FOUND THEN

DBMS_OUTPUT.PUT_LINE('编号错误,没有找到相应雇员!');

WHEN OTHERS THEN

DBMS_OUTPUT.PUT_LINE('发生其他错误!');

END;

执行结果为:

Sql代码

1.编号错误,没有找到相应雇员!

2.  PL/SQL 过程已成功完成。

编号错误,没有找到相应雇员!

PL/SQL 过程已成功完成。

说明:在以上查询中,因为编号为1234的雇员不存在,所以将发生类型为“NO_DATA_

FOUND”的异常。“NO_DATA_FOUND”是系统预定义的错误类型,EXCEPTION部分下的WHEN语句将捕捉到该异常,并执行相应代码部分。在本例中,输出用户自定义的错误信息“编号错误,没有找到相应雇员!”。如果发生其他类型的错误,将执行OTHERS条件下的代码部分,显示“发生其他错误!”。

【训练2】  由程序代码显示系统错误。

Sql代码

1.SET SERVEROUTPUT ON

2.DECLARE

3.v_temp NUMBER(5):=1;

4.BEGIN

5.v_temp:=v_temp/0;

6.EXCEPTION

7.  WHEN OTHERS THEN

8.DBMS_OUTPUT.PUT_LINE('发生系统错误!');

9.  DBMS_OUTPUT.PUT_LINE('错误代码:'|| SQLCODE( ));

10.  DBMS_OUTPUT.PUT_LINE('错误信息:' ||SQLERRM( ));

11.  END;

SET SERVEROUTPUT ON

DECLARE

v_temp NUMBER(5):=1;

BEGIN

v_temp:=v_temp/0;

EXCEPTION

WHEN OTHERS THEN

DBMS_OUTPUT.PUT_LINE('发生系统错误!');

DBMS_OUTPUT.PUT_LINE('错误代码:'|| SQLCODE( ));

DBMS_OUTPUT.PUT_LINE('错误信息:' ||SQLERRM( ));

END;

执行结果为:

Sql代码

1.发生系统错误!

2.  错误代码:?1476

3.  错误信息:ORA-01476: 除数为 0

4.  PL/SQL 过程已成功完成。

发生系统错误!

错误代码:?1476

错误信息:ORA-01476: 除数为 0

PL/SQL 过程已成功完成。

说明:程序运行中发生除零错误,由WHEN

OTHERS捕捉到,执行用户自己的输出语句显示错误信息,然后正常结束。在错误处理部分使用了预定义函数SQLCODE(

)和SQLERRM( )来进一步获得错误的代码和种类信息。

预定义错误

Oracle的系统错误很多,但只有一部分常见错误在标准包中予以定义。定义的错误可以在EXCEPTION部分通过标准的错误名来进行判断,并进行异常处理。常见的系统预定义异常如下所示。

Sql代码

1.错 误 名 称 错误代码  错 误 含 义

2.CURSOR_ALREADY_OPEN ORA_06511  试图打开已经打开的游标

3.INVALID_CURSOR  ORA_01001

试图使用没有打开的游标

4.DUP_VAL_ON_INDEX  ORA_00001  保存重复值到惟一索引约束的列中

5.ZERO_DIVIDE ORA_01476  发生除数为零的除法错误

6.INVALID_NUMBER  ORA_01722

试图对无效字符进行数值转换

7.ROWTYPE_MISMATCH  ORA_06504  主变量和游标的类型不兼容

8.VALUE_ERROR ORA_06502  转换、截断或算术运算发生错误

9.TOO_MANY_ROWS  ORA_01422

SELECT…INTO…语句返回多于一行的数据

10.NO_DATA_FOUND  ORA_01403

SELECT…INTO…语句没有数据返回

11.TIMEOUT_ON_RESOURCE ORA_00051  等待资源时发生超时错误

12.TRANSACTION_BACKED_OUT  ORA_00060

由于死锁,提交失败

13.STORAGE_ERROR  ORA_06500

发生内存错误

14.PROGRAM_ERROR  ORA_06501

发生PL/SQL内部错误

15.NOT_LOGGED_ON  ORA_01012

试图操作未连接的数据库

16.LOGIN_DENIED  ORA_01017

在连接时提供了无效用户名或口令

比如,如果程序向表的主键列插入重复值,则将发生DUP_VAL_ON_INDEX错误。

如果一个系统错误没有在标准包中定义,则需要在说明部分定义,语法如下:

错误名 EXCEPTION;

定义后使用PRAGMA

EXCEPTION_INIT来将一个定义的错误同一个特别的Oracle错误代码相关联,就可以同系统预定义的错误一样使用了。语法如下:

PRAGMA EXCEPTION_INIT(错误名,- 错误代码);

【训练1】  定义新的系统错误类型。

Sql代码

1.SET SERVEROUTPUT ON

2.  DECLARE

3.  V_ENAME VARCHAR2(10);

4.  NULL_INSERT_ERROR EXCEPTION;

5.  PRAGMA EXCEPTION_INIT(NULL_INSERT_ERROR,-1400);

6.  BEGIN

7.  INSERT INTO EMP(EMPNO) VALUES(NULL);

8.EXCEPTION

9.WHEN NULL_INSERT_ERROR THEN

10.  DBMS_OUTPUT.PUT_LINE('无法插入NULL值!');

11.  WHEN OTHERS

THEN

12.  DBMS_OUTPUT.PUT_LINE('发生其他系统错误!');

13.END;

SET SERVEROUTPUT ON

DECLARE

V_ENAME VARCHAR2(10);

NULL_INSERT_ERROR EXCEPTION;

PRAGMA

EXCEPTION_INIT(NULL_INSERT_ERROR,-1400);

BEGIN

INSERT INTO EMP(EMPNO) VALUES(NULL);

EXCEPTION

WHEN NULL_INSERT_ERROR THEN

DBMS_OUTPUT.PUT_LINE('无法插入NULL值!');

WHEN OTHERS  THEN

DBMS_OUTPUT.PUT_LINE('发生其他系统错误!');

END;

执行结果为:

Sql代码

1.无法插入NULL值!

2.  PL/SQL 过程已成功完成。

无法插入NULL值!

PL/SQL 过程已成功完成。

说明:NULL_INSERT_ERROR是自定义异常,同系统错误1400相关联。

自定义异常

程序设计者可以利用引发异常的机制来进行程序设计,自己定义异常类型。可以在声明部分定义新的异常类型,定义的语法是:

错误名 EXCEPTION;

用户定义的错误不能由系统来触发,必须由程序显式地触发,触发的语法是:

RAISE 错误名;

RAISE也可以用来引发模拟系统错误,比如,RAISE ZERO_DIVIDE将引发模拟的除零错误。

使用RAISE_APPLICATION_ERROR函数也可以引发异常。该函数要传递两个参数,第一个是用户自定义的错误编号,第二个参数是用户自定义的错误信息。使用该函数引发的异常的编号应该在20

000和20 999之间选择。

自定义异常处理错误的方式同前。

【训练1】  插入新雇员,限定插入雇员的编号在7000~8000之间。

Java代码

1.SET SERVEROUTPUT ON

2.DECLARE

3.new_no NUMBER(10);

4.new_excp1 EXCEPTION;

5.new_excp2 EXCEPTION;

6.BEGIN

7.new_no:=6789;

8.INSERT INTO emp(empno,ename)

9.  VALUES(new_no, '小郑');

10.  IF new_no<7000 THEN

11.  RAISE new_excp1;

12.  END IF;

13.  IF new_no>8000 THEN

14.  RAISE new_excp2;

15.  END IF;

16.  COMMIT;

17.EXCEPTION

18.WHEN new_excp1  THEN

19.  ROLLBACK;

20.  DBMS_OUTPUT.PUT_LINE('雇员编号小于7000的下限!');

21.  WHEN new_excp2

THEN

22.  ROLLBACK;

23.  DBMS_OUTPUT.PUT_LINE('雇员编号超过8000的上限!');

24.  END;

SET SERVEROUTPUT ON

DECLARE

new_no NUMBER(10);

new_excp1 EXCEPTION;

new_excp2 EXCEPTION;

BEGIN

new_no:=6789;

INSERT INTO  emp(empno,ename)

VALUES(new_no, '小郑');

IF new_no<7000 THEN

RAISE new_excp1;

END IF;

IF new_no>8000 THEN

RAISE new_excp2;

END IF;

COMMIT;

EXCEPTION

WHEN new_excp1  THEN

ROLLBACK;

DBMS_OUTPUT.PUT_LINE('雇员编号小于7000的下限!');

WHEN new_excp2  THEN

ROLLBACK;

DBMS_OUTPUT.PUT_LINE('雇员编号超过8000的上限!');

END;

执行结果为:

雇员编号小于7000的下限!

PL/SQL 过程已成功完成。

说明:在此例中,自定义了两个异常:new_excp1和new_excp2,分别代表编号小于7000和编号大于8000的错误。在程序中通过判断编号大小,产生对应的异常,并在异常处理部分回退插入操作,然后显示相应的错误信息。

【训练2】

使用RAISE_APPLICATION_ERROR函数引发系统异常。

Sql代码

1.SET SERVEROUTPUT ON

2.DECLARE

3.New_no NUMBER(10);

4.BEGIN

5.  New_no:=6789;

6. INSERT INTO  emp(empno,ename)

7.  VALUES(new_no, 'JAMES');

8.IF new_no<7000 THEN

9.  ROLLBACK;

10.  RAISE_APPLICATION_ERROR(-20001, '编号小于7000的下限!');

11.  END IF;

12.  IF new_no>8000

THEN

13.  ROLLBACK;

14.  RAISE_APPLICATION_ERROR (-20002, '编号大于8000的下限!');

15.  END IF;

16.END;

SET SERVEROUTPUT ON

DECLARE

New_no NUMBER(10);

BEGIN

New_no:=6789;

INSERT INTO emp(empno,ename)

VALUES(new_no, 'JAMES');

IF new_no<7000 THEN

ROLLBACK;

RAISE_APPLICATION_ERROR(-20001, '编号小于7000的下限!');

END IF;

IF new_no>8000 THEN

ROLLBACK;

RAISE_APPLICATION_ERROR

(-20002, '编号大于8000的下限!');

END IF;

END;

执行结果为:

Sql代码

1.DECLARE

2.  *

3.  ERROR 位于第 1 行:

4.  ORA-20001: 编号小于7000的下限!

5.  ORA-06512: 在line 9

DECLARE

*

ERROR 位于第 1 行:

ORA-20001: 编号小于7000的下限!

ORA-06512: 在line 9

说明:在本训练中,使用RAISE_APPLICATION_ERROR引发自定义异常,并以系统错误的方式进行显示。错误编号为20001和20002。

注意:同上一个训练比较,此种方法不需要事先定义异常,可直接引发。

可以参考下面的程序片断将出错信息记录到表中,其中,errors为记录错误信息的表,SQLCODE为发生异常的错误编号,SQLERRM为发生异常的错误信息。

DECLARE

v_error_code  NUMBER;

v_error_message  VARCHAR2(255);

BEGIN

...

EXCEPTION

...

WHEN OTHERS THEN

v_error_code := SQLCODE

;

v_error_message :=

SQLERRM ;

INSERT INTO errors

VALUES(v_error_code,

v_error_message);

END;

【练习1】修改雇员的工资,通过引发异常控制修改范围在600~6000之间。

阶段训练

【训练1】  将雇员从一个表复制到另一个表。

步骤1:创建一个结构同EMP表一样的新表EMP1:

CREATE TABLE emp1 AS SELECT * FROM SCOTT.EMP WHERE 1=2;

步骤2:通过指定雇员编号,将雇员由EMP表移动到EMP1表:

Sql代码

1.SET SERVEROUTPUT ON

2.DECLARE

3.v_empno NUMBER(5):=7788;

4.emp_rec emp%ROWTYPE;

5.BEGIN

6. SELECT * INTO emp_rec FROM emp WHERE empno=v_empno;

7. DELETE FROM emp WHERE empno=v_empno;

8.INSERT INTO emp1 VALUES emp_rec;

9. IF SQL%FOUND THEN

10.  COMMIT;

11.  DBMS_OUTPUT.PUT_LINE('雇员复制成功!');

12. ELSE

13.  ROLLBACK;

14.  DBMS_OUTPUT.PUT_LINE('雇员复制失败!');

15. END IF;

16.END;

SET SERVEROUTPUT ON

DECLARE

v_empno NUMBER(5):=7788;

emp_rec emp%ROWTYPE;

BEGIN

SELECT * INTO emp_rec FROM emp WHERE

empno=v_empno;

DELETE FROM emp WHERE empno=v_empno;

INSERT INTO emp1 VALUES emp_rec;

IF SQL%FOUND THEN

COMMIT;

DBMS_OUTPUT.PUT_LINE('雇员复制成功!');

ELSE

ROLLBACK;

DBMS_OUTPUT.PUT_LINE('雇员复制失败!');

END IF;

END;

执行结果为:

雇员复制成功!

PL/SQL 过程已成功完成。

步骤2:显示复制结果:

SELECT empno,ename,job FROM emp1;

执行结果为:

Sql代码

1.EMPNO ENAME  JOB

2.------------- -------------- ----------------

3.  7788

SCOTT  ANALYST

EMPNO ENAME  JOB

-------------- -------------- ----------------

7788

SCOTT  ANALYST

说明:emp_rec变量是根据emp表定义的记录变量,SELECT...INTO...语句将整个记录传给该变量。INSERT语句将整个记录变量插入emp1表,如果插入成功(SQL%FOUND为真),则提交事务,否则回滚撤销事务。试修改雇员编号为7902,重新执行以上程序。

【训练2】  输出雇员工资,雇员工资用不同高度的*表示。

输入并执行以下程序:

Sql代码

1.SET SERVEROUTPUT ON

2.BEGIN

3. FOR re IN (SELECT ename,sal FROM EMP)

LOOP

4.  DBMS_OUTPUT.PUT_LINE(rpad(re.ename,12,'

')||rpad('*',re.sal/100,'*'));

5. END LOOP;

6.END;

SET SERVEROUTPUT ON

BEGIN

FOR re IN (SELECT ename,sal FROM EMP)

LOOP

DBMS_OUTPUT.PUT_LINE(rpad(re.ename,12,'

')||rpad('*',re.sal/100,'*'));

END LOOP;

END;

输出结果为:

Sql代码

1.SMITH  ********

2.ALLEN  ****************

3.WARD  *************

4.JONES  ******************************

5.MARTIN  *************

6.BLAKE  *****************************

7.CLARK  *****************************

8.SCOTT  ******************************

9.KING  **************************************************

10.TURNER  ***************

11.ADAMS  ***********

12.JAMES  **********

13.FORD  ******************************

14.MILLER  *************

15.  执行结果为:

16.  PL/SQL 过程已成功完成。

SMITH  ********

ALLEN  ****************

WARD  *************

JONES  ******************************

MARTIN  *************

BLAKE  *****************************

CLARK  *****************************

SCOTT  ******************************

KING  **************************************************

TURNER  ***************

ADAMS  ***********

JAMES  **********

FORD  ******************************

MILLER  *************

执行结果为:

PL/SQL 过程已成功完成。

说明:第一个rpad函数产生对齐效果,第二个rpad函数根据工资额产生不同数目的*。该程序采用了隐式的简略游标循环形式。

【训练3】  编写程序,格式化输出部门信息。

输入并执行如下程序:

Sql代码

1.SET SERVEROUTPUT ON

2.  DECLARE

3.  v_count number:=0;

4.  CURSOR dept_cursor IS SELECT * FROM dept;

5.  BEGIN

6.  DBMS_OUTPUT.PUT_LINE('部门列表');

7.DBMS_OUTPUT.PUT_LINE('---------------------------------');

8.  FOR Dept_record IN dept_cursor LOOP

9.  DBMS_OUTPUT.PUT_LINE('部门编号:'||

Dept_record.deptno);

10.  DBMS_OUTPUT.PUT_LINE('部门名称:'||

Dept_record.dname);

11.  DBMS_OUTPUT.PUT_LINE('所在城市:'|| Dept_record.loc);

12.DBMS_OUTPUT.PUT_LINE('---------------------------------');

13.  v_count:= v_count+1;

14.  END LOOP;

15.  DBMS_OUTPUT.PUT_LINE('共有'||to_char(v_count)||'个部门!');

16.  END;

SET SERVEROUTPUT ON

DECLARE

v_count number:=0;

CURSOR dept_cursor IS SELECT * FROM dept;

BEGIN

DBMS_OUTPUT.PUT_LINE('部门列表');

DBMS_OUTPUT.PUT_LINE('---------------------------------');

FOR

Dept_record IN dept_cursor LOOP

DBMS_OUTPUT.PUT_LINE('部门编号:'|| Dept_record.deptno);

DBMS_OUTPUT.PUT_LINE('部门名称:'|| Dept_record.dname);

DBMS_OUTPUT.PUT_LINE('所在城市:'||

Dept_record.loc);

DBMS_OUTPUT.PUT_LINE('---------------------------------');

v_count:=

v_count+1;

END LOOP;

DBMS_OUTPUT.PUT_LINE('共有'||to_char(v_count)||'个部门!');

END;

输出结果为:

Sql代码

1.部门列表

2.------------------------------------

3.部门编号:10

4.部门名称:ACCOUNTING

5.所在城市:NEW YORK

6.------------------------------------

7.部门编号:20

8.部门名称:RESEARCH

9.所在城市:DALLAS

10....

11.共有4个部门!

12.PL/SQL 过程已成功完成。

部门列表

------------------------------------

部门编号:10

部门名称:ACCOUNTING

所在城市:NEW YORK

------------------------------------

部门编号:20

部门名称:RESEARCH

所在城市:DALLAS

...

共有4个部门!

PL/SQL 过程已成功完成。

说明:该程序中将字段内容垂直排列。V_count变量记录循环次数,即部门个数。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值