/* 游标是sql的一个内存工作区,有系统或用户以变量的形式定义。游标的作用就是用于临时存储从数据库中提取 的数据块。 游标有两种类型:显示游标和隐式游标。 一次从数据库中提取一行数据,对于这种形式的查询和DML操作,系统都会使用一个隐式游标, 如果要提取多行数据,就要有程序员定义一个显示游标,并通过与游标有关的语句进行处理。 显示游标对应一个返回结果为多行多列的SELECT语句。 游标一旦打开,数据就从数据库中传送到游标变量中,然后应用程序再从游标中分解出需要的数据,并进行处理。 */ /** 隐式游标 DML操作和单行SELECT语句使用隐式游标 INSERT,UPDATE,DELETE,SELECT...INTO... 当系统使用一个隐式游标时,可以通过隐式游标的属性来了解操作的状态和结果,进而控制程序的流程。 隐式游标可以使用名字sql来访问,但要注意,通过sql游标名总是只能访问前一个DML操作或单行SELECT操作的游标属性。 所以通常在刚刚执行完操作之后哦,立即使用sql游标名来访问属性。 游标的属性有以下四种: 隐式游标的属性 返回值类型 意 义 sql%rOWCOUNT 整型 代表DML 语句成功执行的数据行数 sql%FOUND 布尔型 值为TRUE 代表插入、删除、更新或单行查询操作成功 sql%NOTFOUND 布尔型 与sql%FOUND 属性返回值相反 sql%ISOPEN 布尔型 DML 执行过程中为真,结束后为假 */ --使用隐式游标的属性,判断对雇员工资的修改是否成功 declare begin update scott.emp emp set emp.sal = sal+100 where empno=7788; if sql%FOUND then dbms_output.put_line('OK'); commit; else dbms_output.put_line('fail'); end if; end; --使用隐式游标的属性,判断修改员工的行数 declare begin update scott.emp emp set emp.sal = sal+1; dbms_output.put_line(sql%rOWCOUNT); end; --输出结果:20 /** 显示游标 显示游标的使用分为以下4个步骤 1. 声明游标 在DECLARE部分按以下格式声明游标: CURSOR 游标名[(参数1 数据类型[,参数2 数据类型...])] IS SELECT语句: 参数是可选部分,所定义的参数可以出现在SELECT语句的WHERE子句中。如果定义了参数, 则必须在打开游标时传递相应的实际参数 SELECT语句是对表或视图的查询语句,甚至也可以是联合查询,可以带WHERE条件、ORDER BY或GROUP BY 等子句,但不能使用INTO子句。在SELECT语句中可以使用在定义游标之前定义的变量 2. 打开游标 在可执行部分,按以下格式打开游标 OPEN 游标名[(实际参数1[,实际参数2...])] 打开游标时,SELECT语句的查询结果就被传递到了游标工作区。 3. 提取数据 在可执行部分,按以下格式将游标工作区中的数据提取到变量中。提取操作必须在打开游标之后进行。 FETCH 游标名 INTO 变量名1,变量2...] 或 FETCH 游标名 INTO 记录变量; 游标打开后有一个指针指向数据区,FETCH语句一次返回指针值得一行数据,要返回多行重复执行,可以使用循环语句来实现。 控制循环可以通过判断游标的属性来进行。 下面对这两种格式进行说明: 1).变量名是用来从游标中提取数据的变量,需要事先定义。变量的个数和类型应该与SELECT语句中的字段变量的个数和类型一致 2).一次将一行数据提取到记录变量中,需要使用%rOWTYPE事先定义记录变量,这种形式使用起来比较方便,不必分别定义和使用 多个变量 定义记录变量的方法如下: 变量名 表名|游标名%rOWTYPE; 其中的表必须存在,游标名也必须先定义 4. 关闭游标 CLOSE 游标名 显示游标名打开后,必须显示地关闭。游标一旦关闭,游标占用的资源就被释放,游标变成无效,必须重新打开才能使用。 */ --用游标提取emp表中7788雇员的名称和职务 declare v_ename varchar(10); v_job varchar(10); cursor emp_cursor IS select emp.ename,emp.job from scott.emp emp; begin open emp_cursor; fetch emp_cursor into v_ename,v_job; DBMS_OUTPUT.PUT_LINE(v_ename||','||v_job); close emp_cursor; end; --执行结果:SCott,ANALYST --用游标变量提取emp中7788雇员的名称和职务 declare cursor emp_cursor is select emp.ename,emp.job from scott.emp emp where emp.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); close emp_cursor; end; --执行结果:SCott,ANALYST --显示工资最高的前三名雇员的名称和工资。 DECLARE v_ename varchar(10); v_sal number(5); cursor emp_cursor is select emp.ename,emp.sal from scott.emp emp where sal>0 order by sal desc ; begin open emp_cursor; for i in 1..3 loop fetch emp_cursor into v_ename,v_sal; DBMS_OUTPUT.PUT_LINE(v_ename||','||v_sal); end loop; close emp_cursor; end; /* 输出结果 KING,5000 SCott,3100 FORD,3000 */ /* 游标循环 1 */ --显示全部雇员的编号和名称。 DECLARE CURSOR emp_cursor is select empno,ename from scott.emp; begin for emp_record in emp_cursor loop DBMS_OUTPUT.PUT_LINE(emp_record.empno||emp_record.ename); end loop; end; /* 游标循环 2 省略游标的定义,游标的SELECT查询语句在循环中直接出现 */ declare begin for re in(select emp.ename from scott.emp emp) loop DBMS_OUTPUT.PUT_LINE(re.ename); end loop; end; /* 显示游标属性 虽然可以使用前面的形式获得游标数据,但是在游标定义以后使用它的一些属性结构控制是一种更为灵活的方法。 显示游标的属性如表: 游标的属性 返回值类型 意 义 %rOWCOUNT 整型 获得FETCH 语句返回的数据行数 %FOUND 布尔型 最近的FETCH 语句返回一行数据则为真,否则为假 %NOTFOUND 布尔型 与%FOUND 属性返回值相反 %ISOPEN 布尔型 游标已经打开时值为真,否则为假 可按照以下形式取得游标的属性 游标名%属性 要判断游标emp_cursor是否处于打开状态,可以使用属性emp_cursor%ISOPEN。如果游标已经打开,则返回值为"真",否则为"假" */ declare v_ename varchar2(10); cursor emp_cursor is select emp.ename from scott.emp 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; --带参数的游标 打开游标的时候的参数 declare v_empno number(5); v_ename varchar2(10); cursor emp_cursor(p_deptno number,p_job varchar2) is select empno,ename from scott.emp emp where emp.deptno=p_deptno and emp.job=p_job; begin open emp_cursor(10,'CLERK'); if emp_cursor%ISOPEN then loop fetch emp_cursor into v_empno,v_ename; dbms_output.put_line(v_empno||'-'|| v_ename); exit when emp_cursor%NOTFOUND; end loop; else DBMS_OUTPUT.PUT_LINE('用户信息:游标没有打开!'); end if; end; --带参数的游标 通过变量向游标传递参数,变量需要先于游标定义,并在游标打开之前赋值 declare v_empno number(5); v_ename varchar(10); v_depno number(5); v_job varchar2(10); cursor emp_cursor IS select emp.empno,emp.ename from scott.emp emp where emp.deptno=v_depno and emp.job=v_job; begin v_depno:=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; --输出结果:7934,MILLER /* 动态SELECT语句和动态游标的用法 对于查询结果为一行的select语句,可以用动态生成查询语句字符串的方法,在程序执行阶段临时生成并执行, execute immediate 查询语句字符串 into 变量1、、 */ declare str varchar2(100); v_ename varchar2(10); begin str := 'select emp.ename from scott.emp emp where emp.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(550); letter char :='A'; begin loop str:='select emp.ename from scott.emp where emp.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; /* 异常处理 错误是在标准包中由系统预定义的标准错误,或是有用户在程序的说明部分自定义的错误。 */ --查询编号为1234的雇员名字 declare v_name varchar2(10); begin select emp.ename into v_name from scott.emp emp where emp.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; --执行结果:无改用户 --由程序代码显示系统错误 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; /* 错 误 名 称 错误代码 错 误 含 义 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 在连接时提供了无效用户名或口令 www. */ --定义新的系统错误类型。 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值!