Oracle游标-提取记录、游标循环
2015年06月07日 09:54:34 zzxiaozhi 阅读数:14219
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;
https://blog.csdn.net/zzxiaozhi/article/details/46398365