Oracle游标-提取记录、游标循环

1. 两种提取数据方式:

FETCH 游标名 INTO 变量名1[,变量名2...]; 

FETCH 游标名 INTO 记录变量; 

第一种格式中的变量名是用来从游标中接收数据的变量,需要事先定义。变量的个数和类型应与SELECT语句中的字段变量的个数和类型一致。 
第二种格式一次将一行数据取到记录变量中,需要使用%ROWTYPE事先定义记录变量,这种形式使用起来比较方便,不必分别定义和使用多个变量。 
定义记录变量的方法如下: 
变量名 表名|游标名%ROWTYPE; 
其中的表必须存在,游标名也必须先定义。 

第一种:变量名

SET SERVEROUTPUT ON
		DECLARE	
 		 v_ename VARCHAR2(10);
 		 v_job VARCHAR2(10);
 		 CURSOR emp_cursor IS 
 		 SELECT ename,job FROM emp WHERE empno=7788;
		 BEGIN
 	 OPEN emp_cursor;
  	FETCH emp_cursor INTO v_ename,v_job;
  		DBMS_OUTPUT.PUT_LINE(v_ename||','||v_job);
  		CLOSE emp_cursor;
		END;

第二种: 记录变量(说明:实例中使用记录变量来接收数据,记录变量由游标变量定义,需要出现在游标定义之后。 )

1.1例子:

SET SERVEROUTPUT ON
		DECLARE
 		 CURSOR emp_cursor IS  SELECT ename,job,sal FROM emp WHERE empno=7788;
 		 emp_record emp_cursor%ROWTYPE;
		BEGIN
OPEN emp_cursor;	
   		FETCH emp_cursor INTO emp_record;
		   DBMS_OUTPUT.PUT_LINE(emp_record.ename||','|| emp_record.job||','|| emp_record.sal);
 		 CLOSE emp_cursor;
		END;
1.2例子
--显示工资最高的前3名雇员的名称和工资。
SET SERVEROUTPUT ON
<span style="white-space:pre">	</span>DECLARE
<span style="white-space:pre">	</span> V_ename VARCHAR2(10);
<span style="white-space:pre">	</span>V_sal NUMBER(5);
<span style="white-space:pre">	</span>CURSOR emp_cursor IS  SELECT ename,sal FROM emp ORDER BY sal DESC;
<span style="white-space:pre">	</span>BEGIN
<span style="white-space:pre">	</span> OPEN emp_cursor;
<span style="white-space:pre">	</span> FOR I IN 1..3 LOOP
<span style="white-space:pre">	</span>   FETCH emp_cursor INTO v_ename,v_sal;
<span style="white-space:pre">	</span> DBMS_OUTPUT.PUT_LINE(v_ename||','||v_sal);
<span style="white-space:pre">	</span>  END LOOP;
<span style="white-space:pre">	</span> CLOSE emp_cursor;
<span style="white-space:pre">	</span> END;

2.游标循环

--2.1 使用特殊的FOR循环形式显示全部雇员的编号和名称。
SET SERVEROUTPUT ON
DECLARE
  CURSOR emp_cursor IS 
  SELECT empno, ename FROM emp;
BEGIN
FOR Emp_record IN emp_cursor LOOP   
    DBMS_OUTPUT.PUT_LINE(Emp_record.empno|| Emp_record.ename);
	END LOOP;
	END;
--2.2 另一种形式的游标循环
SET SERVEROUTPUT ON 
BEGIN
 FOR re IN (SELECT ename FROM EMP)  LOOP
  DBMS_OUTPUT.PUT_LINE(re.ename)
 END LOOP;
END;

3. 显式游标属性

游标定义以后使用它的一些属性来进行结构控制是一种更为灵活的方法。显式游标的属性如下所示。

游标的属性	返回值类型	意    义
%ROWCOUNT	整型	获得FETCH语句返回的数据行数
%FOUND	布尔型	最近的FETCH语句返回一行数据则为真,否则为假
%NOTFOUND	布尔型	与%FOUND属性返回值相反
%ISOPEN	布尔型	游标已经打开时值为真,否则为假

可按照以下形式取得游标的属性: 
游标名%属性 
要判断游标emp_cursor是否处于打开状态,可以使用属性emp_cursor%ISOPEN。如果游标已经打开,则返回值为“真”,否则为“假”。具体可参照以下的训练。

SET SERVEROUTPUT ON
DECLARE
  V_ename VARCHAR2(10);
  CURSOR emp_cursor IS 
  SELECT ename FROM emp;
BEGIN
 OPEN emp_cursor;
 IF emp_cursor%ISOPEN THEN
LOOP
   FETCH emp_cursor INTO v_ename;
   EXIT WHEN emp_cursor%NOTFOUND;
   DBMS_OUTPUT.PUT_LINE(to_char(emp_cursor%ROWCOUNT)||'-'||v_ename);
  END LOOP;
 ELSE
  DBMS_OUTPUT.PUT_LINE('用户信息:游标没有打开!');
 END IF;
 CLOSE  emp_cursor;
END;
说明:本例使用emp_cursor%ISOPEN判断游标是否打开;使用emp_cursor%ROWCOUNT获得到目前为止FETCH语句返回的数据行数并输出;使用循环来获取数据,在循环体中使用FETCH语句;使用emp_cursor%NOTFOUND判断FETCH语句是否成功执行,当FETCH语句失败时说明数据已经取完,退出循环。

4.游标参数的传递

--4.1带参数的游标。
SET SERVEROUTPUT ON
	DECLARE
		V_empno NUMBER(5);
		V_ename VARCHAR2(10);
	CURSOR 	emp_cursor(p_deptno NUMBER, 	p_job VARCHAR2) IS
	    SELECT	empno, ename FROM emp
	WHERE	deptno = p_deptno AND job = p_job;
BEGIN
 OPEN emp_cursor(10, 'CLERK');
LOOP
 FETCH emp_cursor INTO v_empno,v_ename;
 EXIT WHEN emp_cursor%NOTFOUND;
 DBMS_OUTPUT.PUT_LINE(v_empno||','||v_ename);
  END LOOP;
END;
说明:游标emp_cursor定义了两个参数:p_deptno代表部门编号,p_job代表职务。语句OPEN emp_cursor(10, 'CLERK')传递了两个参数值给游标,即部门为10、职务为CLERK,所以游标查询的内容是部门10的职务为CLERK的雇员。循环部分用于显示查询的内容。

--4.2通过变量向游标传递参数,但变量需要先于游标定义,并在游标打开之前赋值
SET SERVEROUTPUT ON
	DECLARE
	v_empno NUMBER(5);
	v_ename VARCHAR2(10);
	v_deptno NUMBER(5);
v_job VARCHAR2(10);
	 CURSOR emp_cursor IS
	SELECT empno, ename FROM emp
	WHERE	deptno = v_deptno AND job = v_job;
	BEGIN
	 v_deptno:=10;
	 v_job:='CLERK';
	 OPEN emp_cursor;
	LOOP
	 FETCH emp_cursor INTO v_empno,v_ename;
	   EXIT WHEN emp_cursor%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(v_empno||','||v_ename);
	 END LOOP;
	END;

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

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

--5.1 动态SELECT查询
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; 

在变量声明部分定义的游标是静态的,不能在程序运行过程中修改。

通过采用动态游标,可以在程序运行阶段随时生成一个查询语句作为游标。要使用动态游标需要先定义一个游标类型,然后声明一个游标变量,游标对应的查询语句可以在程序的执行过程中动态地说明。 
定义游标类型的语句如下: 
TYPE 游标类型名 REF CURSOR; 
声明游标变量的语句如下: 
游标变量名 游标类型名; 
在可执行部分可以如下形式打开一个动态游标: 
OPEN 游标变量名 FOR 查询语句字符串; 

--按名字中包含的字母顺序分组显示雇员信息。
declare 
 type cur_type is ref cursor;
 cur cur_type;
 rec scott.emp%rowtype;
 str varchar2(50);
 letter char:= 'A';
begin
<span style="white-space:pre">	</span>loop  <span style="white-space:pre">		</span>
<span style="white-space:pre">	</span> str:= 'select ename from emp where ename like ''%'||letter||'%''';
<span style="white-space:pre">	</span> open cur for str;
<span style="white-space:pre">	</span> dbms_output.put_line('包含字母'||letter||'的名字:');
<span style="white-space:pre">	</span>  loop
<span style="white-space:pre">	</span> fetch cur into rec.ename;
<span style="white-space:pre">	</span> exit when cur%notfound;
<span style="white-space:pre">	</span>dbms_output.put_line(rec.ename);
end loop;
  exit when letter='Z';
  letter:=chr(ascii(letter)+1);
 end loop;
end;
说明:使用了二重循环,在外循环体中,动态生成游标的SELECT语句,然后打开。通过语句letter:=chr(ascii(letter)+1)可获得字母表中的下一个字母。 

6. 异常处理
凡是出现在WHEN后面的错误都是可以捕捉到的错误,其他未被捕捉到的错误,将在WHEN OTHERS部分进行统一处理,OTHENS必须是EXCEPTION部分的最后一个错误处理分支。如要在该分支中进一步判断错误种类,可以通过使用预定义函数SQLCODE( )和SQLERRM( )来获得系统错误号和错误信息。 
如果在程序的子块中发生了错误,但子块没有错误处理部分,则错误会传递到主程序中。 

--6.1查询编号为1234的雇员名字。
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;

执行结果为:

编号错误,没有找到相应雇员!
	PL/SQL 过程已成功完成。
6.2由程序代码显示系统错误。 
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;

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

错 误 名 称	错误代码	错 误 含 义
CURSOR_ALREADY_OPEN	ORA_06511	试图打开已经打开的游标
INVALID_CURSOR	ORA_01001	试图使用没有打开的游标
DUP_VAL_ON_INDEX	ORA_00001	保存重复值到惟一索引约束的列中
ZERO_DIVIDE	ORA_01476	发生除数为零的除法错误
INVALID_NUMBER	ORA_01722	试图对无效字符进行数值转换
ROWTYPE_MISMATCH	ORA_06504	主变量和游标的类型不兼容
VALUE_ERROR	ORA_06502	转换、截断或算术运算发生错误
TOO_MANY_ROWS	ORA_01422	SELECT…INTO…语句返回多于一行的数据
NO_DATA_FOUND	ORA_01403	SELECT…INTO…语句没有数据返回
TIMEOUT_ON_RESOURCE	ORA_00051	等待资源时发生超时错误
TRANSACTION_BACKED_OUT	ORA_00060	由于死锁,提交失败
STORAGE_ERROR	ORA_06500	发生内存错误
PROGRAM_ERROR	ORA_06501	发生PL/SQL内部错误
NOT_LOGGED_ON	ORA_01012	试图操作未连接的数据库
LOGIN_DENIED	ORA_01017	在连接时提供了无效用户名或口令

如果一个系统错误没有在标准包中定义,则需要在说明部分定义,语法如下: 
错误名 EXCEPTION; 
定义后使用PRAGMA EXCEPTION_INIT来将一个定义的错误同一个特别的Oracle错误代码相关联,就可以同系统预定义的错误一样使用了。语法如下:  PRAGMA EXCEPTION_INIT(错误名,- 错误代码); 

--定义新的系统错误类型。 
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;
--执行结果为: 
无法插入NULL值!
	PL/SQL 过程已成功完成。

8.  自定义异常

程序设计者可以利用引发异常的机制来进行程序设计,自己定义异常类型。可以在声明部分定义新的异常类型,定义的语法是: 
错误名 EXCEPTION; 
用户定义的错误不能由系统来触发,必须由程序显式地触发,触发的语法是: 
RAISE 错误名; 
RAISE也可以用来引发模拟系统错误,比如,RAISE ZERO_DIVIDE将引发模拟的除零错误。 
使用RAISE_APPLICATION_ERROR函数也可以引发异常。该函数要传递两个参数,第一个是用户自定义的错误编号,第二个参数是用户自定义的错误信息。使用该函数引发的异常的编号应该在20 000和20 999之间选择。 
自定义异常处理错误的方式同前。 

--8.1插入新雇员,限定插入雇员的编号在7000~8000之间。
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的错误。在程序中通过判断编号大小,产生对应的异常,并在异常处理部分回退插入操作,然后显示相应的错误信息。

--使用RAISE_APPLICATION_ERROR函数引发系统异常。
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;

--执行结果为: 
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; 



  • 9
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值