
---参考:《Oracle 11g SQL和 PLSQL从入门到精通》
一:隐含游标
二:显示游标
当执行SELECT、INSERT、UPDATE、以及DELETE语句时,Oracle Server会为这些SQL语句分配相应的上下文区(Context Area),并且上下文区解析并执行相应的SQL语句。
游标是指向上下文区的指针。
游标包含隐含游标和显示游标两种类型。

一:隐含游标
1.1 SQL%ISOPEN
1.2 SQL%FOUND
1.3 SQL%NOTFOUND
1.4 SQL%ROWCOUNT
隐含游标又被称为SQL游标,专门用于处理SELECT INTO、INSERT、UPDATE、DELETE语句。
显示游标多处理多行的SELECT语句。
当在PL/SQL块中执行INSERT、UPDATE、DELETE语句时,为了取得DML语句作用的结果,需要使用SQL游标属性。
1.SQL%ISOPEN
该游标属性用于确定SQL游标是否已经打开。当PL/SQL块中执行SELECT INTO、INSERT、UPDATE、DELETE语句时,Oracle会隐含的打开游标,并且在语句执行完成之后隐含的关闭游标。
所有对于开发人员来说该属性的值永远都是FLASE,并且在开发PL/SQL应用时不需要使用该游标属性。
2.SQL%FOUND
该游标属性用于确定SQL语句执行是否成功。
SQL语句执行是否成功是根据作用行来判断的,当SQL语句有作用行时,其属性值为TRUE;
当SQL语句没有作用行时,其属性值为FALSE。
示例:
SQL> set serveroutput onDECLAREv_deptno emp.deptno%TYPE := &no;BEGINUPDATE emp SET sal = sal * 1.1 WHERE deptno = v_deptno;IF SQL%FOUND THENdbms_output.put_line('update ' || SQL%ROWCOUNT || ' line');ELSEdbms_output.put_line('There are no employees in this department');END IF;END;/Enter value for no: 10old 2: v_deptno emp.deptno%TYPE := &no;new 2: v_deptno emp.deptno%TYPE := 10;update 3 linePL/SQL procedure successfully completed.
3.SQL%NOTFOUND
该游标属性用于确定SQL语句执行是否成功。
SQL语句执行是否成功是根据作用行来判断的,当SQL语句有作用行时,其属性值为FALSE;
当SQL语句没有作用行时,其属性值为TRUE。
示例:
SQL> set serveroutput onDECLAREv_deptno emp.deptno%TYPE := &no;BEGINUPDATE emp SET sal = sal * 1.1 WHERE deptno = v_deptno;IF SQL%NOTFOUND THENdbms_output.put_line('There are no employees in this department');ELSEdbms_output.put_line('update ' || SQL%ROWCOUNT || ' line');END IF;END;/Enter value for no: 10old 2: v_deptno emp.deptno%TYPE := &no;new 2: v_deptno emp.deptno%TYPE := 10;update 3 linePL/SQL procedure successfully completed.
4 SQL%ROWCOUNT
该游标属性用于返回SQL语句所作用的总计行数。
SQL> set serveroutput onDECLAREv_deptno emp.deptno%TYPE := &no;BEGINUPDATE emp set sal = sal * 1.1 where deptno = v_deptno;dbms_output.put_line('update '||SQL%ROWCOUNT || ' lines');END;Enter value for no: 10old 2: v_deptno emp.deptno%TYPE := &no;new 2: v_deptno emp.deptno%TYPE := 10;update 3 linesPL/SQL procedure successfully completed.

二:显示游标
2.1使用显示游标
2.2游标FOR循环
2.3参数游标
2.4更新或删除游标行
2.5游标变量
2.6使用批量提取
2.7使用CURSOR表达式
隐含游标用于处理单行SELECT INTO和DML语句。
显示游标用于处理SELECT返回的多行数据。
显示游标属性:
%ISOPEN、%FOUND、%NOTFOUND和%ROWCOUNT四个属性。
(1)%ISOPEN
该属性用于检测游标是否已经打开。如果游标已经打开,则返回TRUE,否则返回FALSE。
IF NOT emp_cursor%ISOPEN THENOPEN emp_cursor;END IF;
(2)%FOUND
该属性用于检测游标结果集是否存在数据。如果存在数据,则返回TRUE,否则返回FALSE。
LOOPFETCH emp_cursor INTO v_name,v_sal;EXIT WHEN NOT emp_cursor%FOUND;...END LOOP;
(3)%NOTFOUND
该属性用于检测游标结果集是否不存在数据。如果不存在数据,则返回TRUE,否则返回FALSE。
LOOPFETCH emp_cursor INTO v_name,v_sal;EXIT WHEN emp_cursor%NOTFOUND;...END LOOP;
(4)%ROWCOUNT
该属性用于返回已提取的实际行数。
LOOPFETCH emp_cursor INTO v_ename,v_deptno;EXIT WHEN emp_cursor%NOTFOUND OR emp_cursor%ROWCOUNT=5;...END LOOP;

2.1使用显示游标
显示游标专用于处理SELECT语句返回的多行数据,使用显示游标包括定义游标、打开游标、提前数据和关闭游标四个阶段。
定义游标:定义游标用于指定游标所对应的SELECT语句,语法如下:
CURSOR cursor_name IS select_statement;
打开游标:打开游标用于执行所对应的SELECT语句,并将行数据存放在游标结果集。语法如下:
OPEN cursor_name;
提取数据:提前数据用于将结果集中的行数据存放到PL/SQL变量。语法如下:
FETCH cursor_name INTO variable1[,variables2,...];
关闭游标:关闭游标用于释放游标结果集的数据。语法如下:
CLOSE cursor_name;
注意:当使用FETCH INTO提取数据时,每次只能提前一行数据,为了提前结果集的所有数据,需要使用循环语句。
示例一:使用标量变量接收游标数据
SQL> set serveroutput onDECLARECURSOR emp_cursor ISSELECT ename, job, sal FROM emp WHERE deptno = &dno;v_ename emp.ename%TYPE;v_sal emp.sal%TYPE;v_job emp.job%TYPE;BEGINOPEN emp_cursor;LOOPFETCH emp_cursorINTO v_ename, v_job, v_sal;EXIT WHEN emp_cursor%NOTFOUND;dbms_output.put_line('Ename is:' || v_ename || ',Job is:' || v_job ||',Sal is:' || v_sal);END LOOP;CLOSE emp_cursor;END;/Enter value for dno: 10old 3: SELECT ename, job, sal FROM emp WHERE deptno = &dno;new 3: SELECT ename, job, sal FROM emp WHERE deptno = 10;Ename is:CLARK,Job is:MANAGER,Sal is:2695Ename is:KING,Job is:PRESIDENT,Sal is:5500Ename is:MILLER,Job is:CLERK,Sal is:1430PL/SQL procedure successfully completed.
示例二:使用PL/SQL记录变量接收游标数据
SQL> set serveroutput onDECLARECURSOR emp_cursor ISSELECT ename, sal from emp ORDER BY sal DESC;emp_record emp_cursor%ROWTYPE;BEGINOPEN emp_cursor;LOOPFETCH emp_cursorINTO emp_record;EXIT WHEN emp_cursor%NOTFOUND OR emp_cursor%ROWCOUNT > &N;dbms_output.put_line('ename is:' || emp_record.ename || ',sal is:' ||emp_record.sal);END LOOP;CLOSE emp_cursor;END;/Enter value for n: 3old 10: EXIT WHEN emp_cursor%NOTFOUND OR emp_cursor%ROWCOUNT > &N;new 10: EXIT WHEN emp_cursor%NOTFOUND OR emp_cursor%ROWCOUNT > 3;ename is:KING,sal is:5500ename is:FORD,sal is:3000ename is:SCOTT,sal is:3000
示例三:使用PL/SQL集合变量接收游标数据
SQL> set serveroutput onDECLARECURSOR emp_cursor ISSELECT ename, sal from emp WHERE lower(job) = lower('&job');TYPE emp_table_type IS TABLE OF emp_cursor%ROWTYPE INDEX BY BINARY_INTEGER;emp_table emp_table_type;i INT;BEGINOPEN emp_cursor;LOOPi := emp_cursor%ROWCOUNT + 1;FETCH emp_cursorINTO emp_table(i);EXIT WHEN emp_cursor%NOTFOUND;dbms_output.put_line('ename is:' || emp_table(i).ename || ',sal is:' || emp_table(i).sal);END LOOP;CLOSE emp_cursor;END;/Enter value for job: managerold 3: SELECT ename, sal from emp WHERE lower(job) = lower('&job');new 3: SELECT ename, sal from emp WHERE lower(job) = lower('manager');ename is:JONES,sal is:2975ename is:BLAKE,sal is:2850ename is:CLARK,sal is:2695PL/SQL procedure successfully completed.

2.2游标FOR循环
当使用游标FOR循环时,会隐含的打开游标、提前数据并关闭游标。语法如下:
FOR record_name IN cursor_name LOOPstatement;...END LOOP;
示例一:在游标FOR循环中引用已定义游标
SQL> set serveroutput onDECLARECURSOR emp_cursor ISSELECT ename, hiredate FROM emp ORDER BY hiredate DESC;BEGINFOR emp_record IN emp_cursor LOOPdbms_output.put_line('ename is :' || emp_record.ename ||',hiredate is:' || emp_record.hiredate);EXIT WHEN emp_cursor%ROWCOUNT = &N;END LOOP;END;/Enter value for n: 3old 8: EXIT WHEN emp_cursor%ROWCOUNT = &N;new 8: EXIT WHEN emp_cursor%ROWCOUNT = 3;ename is :ADAMS,hiredate is:23-MAY-87ename is :SCOTT,hiredate is:19-APR-87ename is :MILLER,hiredate is:23-JAN-82PL/SQL procedure successfully completed.
示例二:在游标FOR循环中直接引用子查询
SQL> set serveroutput onBEGINFOR emp_record IN (SELECT ename, hiredate, rownumFROM empORDER BY hiredate) LOOPdbms_output.put_line('ename is:' || emp_record.ename ||',hiredate is:' || emp_record.hiredate);EXIT WHEN emp_record.rownum = &n;END LOOP;END;/Enter value for n: 3old 7: EXIT WHEN emp_record.rownum = &n;new 7: EXIT WHEN emp_record.rownum = 3;ename is:SMITH,hiredate is:17-DEC-80ename is:ALLEN,hiredate is:20-FEB-81ename is:WARD,hiredate is:22-FEB-81PL/SQL procedure successfully completed.

2.3参数游标
指带有参数的游标,使用不同参数值可以生产不同的游标结果集。
示例:
SQL> set serveroutput onDECLARECURSOR emp_cursor(dno NUMBER) ISSELECT ename, job FROM emp WHERE deptno = dno;BEGINFOR emp_record IN emp_cursor(&dno) LOOPdbms_output.put_line('ename is:' || emp_record.ename || 'job is:' ||emp_record.job);END LOOP;END;/Enter value for dno: 20old 5: FOR emp_record IN emp_cursor(&dno) LOOPnew 5: FOR emp_record IN emp_cursor(20) LOOPename is:SMITHjob is:CLERKename is:JONESjob is:MANAGERename is:SCOTTjob is:ANALYSTename is:ADAMSjob is:CLERKename is:FORDjob is:ANALYSTPL/SQL procedure successfully completed.

2.4更新或删除游标行
定义游标必须带有FOR UPDATE子句,并且在更新或删除游标行时必须带有WHERE CURRENT OF子句。
示例一:更新游标行
DECLARECURSOR emp_cursor ISSELECT ename, sal, deptno FROM emp FOR UPDATE;dno INT := &no;BEGINFOR emp_record IN emp_cursor LOOPIF emp_record.deptno = dno THENdbms_output.put_line('ename is:' || emp_record.ename || ',sal is:' ||emp_record.sal);UPDATE emp set sal = sal * 1.1 WHERE CURRENT OF emp_cursor;END IF;END LOOP;END;/Enter value for no: 10old 4: dno INT := &no;new 4: dno INT := 10;ename is:CLARK,sal is:2695ename is:KING,sal is:5500ename is:MILLER,sal is:1430PL/SQL procedure successfully completed.
示例二:删除游标行
DECLARECURSOR emp_cursor ISSELECT ename FROM emp FOR UPDATE;name VARCHAR2(10) := lower('&name');BEGINFOR emp_record IN emp_cursor LOOPIF lower(emp_record.ename) = name THENDELETE FROM emp WHERE CURRENT OF emp_cursor;ELSEdbms_output.put_line('name is:' || emp_record.ename);END IF;END LOOP;END;/Enter value for name: scottold 4: name VARCHAR2(10) := lower('&name');new 4: name VARCHAR2(10) := lower('scott');name is:SMITHname is:ALLENname is:WARDname is:JONESname is:MARTINname is:BLAKEname is:CLARKname is:KINGname is:TURNERname is:ADAMSname is:JAMESname is:FORDname is:MILLERPL/SQL procedure successfully completed.
示例三:使用OF子句在特定表上加行共享锁
DECLARECURSOR emp_cursor ISSELECT a.dname, b.enameFROM dept aJOIN emp bON a.deptno = b.deptnoFOR UPDATE OF b.deptno;name varchar2(10) := LOWER('&name');BEGINFOR emp_record IN emp_cursor LOOPIF LOWER(emp_record.dname) = name THENdbms_output.put_line('ename is:' || emp_record.ename);DELETE FROM emp WHERE CURRENT OF emp_cursor;END IF;END LOOP;END;/Enter value for name: SALESold 8: name varchar2(10) := LOWER('&name');new 8: name varchar2(10) := LOWER('SALES');ename is:ALLENename is:WARDename is:MARTINename is:BLAKEename is:TURNERename is:JAMESPL/SQL procedure successfully completed.

2.5游标变量
游标变量是基于REF CURSOR类型所定义的变量,它实际是指向内存地址的指针。
示例一:使用无返回类型的游标变量
DECLARETYPE ref_cursor_type IS REF CURSOR;ref_cursor ref_cursor_type;v1 NUMBER(6);v2 VARCHAR2(10);BEGINOPEN ref_cursor FORSELECT &col1 col1, &col2 col2 FROM &table WHERE &cond;LOOPFETCH ref_cursorINTO v1, v2;EXIT WHEN ref_cursor%NOTFOUND;dbms_output.put_line('col1=' || v1 || ',col2=' || v2);END LOOP;CLOSE ref_cursor;END;/Enter value for col1: empnoEnter value for col2: enameEnter value for table: empEnter value for cond: deptno=10old 8: SELECT &col1 col1, &col2 col2 FROM &table WHERE &cond;new 8: SELECT empno col1, ename col2 FROM emp WHERE deptno=10;col1=7782,col2=CLARKcol1=7839,col2=KINGcol1=7934,col2=MILLERPL/SQL procedure successfully completed.
示例二:使用有返回类型的游标变量
DECLARETYPE emp_cursor_type IS REF CURSOR RETURN emp%ROWTYPE;emp_cursor emp_cursor_type;emp_record emp%ROWTYPE;BEGINOPEN emp_cursor FORSELECT * FROM emp WHERE deptno = &dno;LOOPFETCH emp_cursorINTO emp_record;EXIT WHEN emp_cursor%NOTFOUND;dbms_output.put_line('ename is:' || emp_record.ename || ',sal is:' ||emp_record.sal);END LOOP;CLOSE emp_cursor;END;/Enter value for dno: 20old 7: SELECT * FROM emp WHERE deptno = &dno;new 7: SELECT * FROM emp WHERE deptno = 20;ename is:SMITH,sal is:800ename is:JONES,sal is:2975ename is:ADAMS,sal is:1100ename is:FORD,sal is:3000PL/SQL procedure successfully completed.

2.6使用批量提取
示例一:使用FETCH...BULK COLLECT提前所有数据
DECLARECURSOR emp_cursor ISSELECT * FROM emp WHERE LOWER(job) = LOWER('&job');TYPE emp_table_type IS TABLE OF emp%ROWTYPE;emp_table emp_table_type;BEGINOPEN emp_cursor;FETCH emp_cursor BULK COLLECTINTO emp_table;CLOSE emp_cursor;FOR i IN 1 .. emp_table.COUNT LOOPdbms_output.put_line('ename is:' || emp_table(i).ename || ',sal is:' || emp_table(i).sal);END LOOP;END;/Enter value for job: clerkold 3: SELECT * FROM emp WHERE LOWER(job) = LOWER('&job');new 3: SELECT * FROM emp WHERE LOWER(job) = LOWER('clerk');ename is:SMITH,sal is:800ename is:ADAMS,sal is:1100ename is:MILLER,sal is:1573PL/SQL procedure successfully completed.
示例二:使用LIMIT子句限制提取行数
DECLARECURSOR emp_cursor ISSELECT * FROM emp;TYPE emp_array_type IS VARRAY(5) OF emp%ROWTYPE;emp_array emp_array_type;BEGINOPEN emp_cursor;LOOPFETCH emp_cursor BULK COLLECTINTO emp_array LIMIT &rows;FOR i IN 1 .. emp_array.COUNT LOOPdbms_output.put_line('ename is:' || emp_array(i).ename || ',sal is:' || emp_array(i).sal);END LOOP;EXIT WHEN emp_cursor%NOTFOUND;END LOOP;CLOSE emp_cursor;END;/Enter value for rows: 3old 10: INTO emp_array LIMIT &rows;new 10: INTO emp_array LIMIT 3;ename is:SMITH,sal is:800ename is:JONES,sal is:2975ename is:CLARK,sal is:2964.5ename is:KING,sal is:6050ename is:ADAMS,sal is:1100ename is:FORD,sal is:3000ename is:MILLER,sal is:1573PL/SQL procedure successfully completed

2.7使用CURSOR表达式
CURSOR表达式用于实现嵌套游标。语法:CURSOR(subquery)
示例:
DECLARECURSOR dept_cursor(no NUMBER) ISSELECT a.dname, CURSOR (SELECT * FROM emp WHERE deptno = a.deptno)FROM dept aWHERE a.deptno = no;TYPE ref_cursor_type IS REF CURSOR;emp_cursor ref_cursor_type;emp_record emp%ROWTYPE;v_dname dept.dname%TYPE;BEGINOPEN dept_cursor(&dno);LOOPFETCH dept_cursorINTO v_dname, emp_cursor;EXIT WHEN dept_cursor%NOTFOUND;dbms_output.put_line('dname is :' || v_dname);LOOPFETCH emp_cursorINTO emp_record;EXIT WHEN emp_cursor%NOTFOUND;dbms_output.put_line('ename is:' || emp_record.ename ||',job is:' || emp_record.job);END LOOP;END LOOP;CLOSE dept_cursor;END;/Enter value for dno: 10old 11: OPEN dept_cursor(&dno);new 11: OPEN dept_cursor(10);dname is :ACCOUNTINGename is:CLARK,job is:MANAGERename is:KING,job is:PRESIDENTename is:MILLER,job is:CLERKPL/SQL procedure successfully completed.
更多数据库相关学习资料,可以查看我的ITPUB博客,网名chenoracle:
http://blog.itpub.net/29785807/





