下面的内容主要来自《Oracle PL/SQL 实例精解》


1.定义


    游标:映射在结果集某一行数据的位置实体。


2.分类


     1.1静态游标


            1)显式游标


            2)隐式游标


     1.2 REF游标


3.隐式游标


     处理过程:


       1)该SQL语句不存在显示游标时,任何特定PL/SQL语句块都会产生一个隐式游标。


       2)它会自动的与DML语句建立关联。


       3)处理隐式游标时,Oracle会自动地执行OPEN\FETCH\CLOSE操作。


       4)最次一次被打开的游标称为SQL游标。     


4.显式游标


     对于返回多行数据的任何查询,都可以定义为显式游标,它常常在PL/SQL语句块的Declare部分对该游标进行声明。Declare部分定义的游标也就是静态游标。


     使用步骤:声明游标、打开游标、检索(取出)、关闭游标。示例如下:

DECLARE
  CURSOR c_emp IS SELECT a.empno,a.ename FROM emp a; --1.声明游标
      r_emp c_emp%ROWTYPE;--定义基于游标的记录变量
BEGIN
       IF c_emp%ISOPEN=FALSE THEN
              OPEN c_emp;--2.打开游标
       END IF;
       LOOP
             FETCH c_emp INTO r_emp; --3.检索数据
             IF c_emp%NOTFOUND THEN
                   EXIT;
             ELSE
                   dbms_output.put_line(r_emp.empno||' '||r_emp.ename);
             END IF;
       END LOOP;
             IF c_emp%ISOPEN THEN
              CLOSE c_emp; --关闭游标
       END IF;
END;

5.游标的四大属性


     SQL%ISOPEN:它是布尔值,如果游标打开,则为true;游标关闭,则为false。对于隐式游标 SQL%Isopen一直是close,隐式游标在DML语句执行时打开,结束时关闭。


     SQL%Found:未执行任何DML语句之前,值为null;DML语句执行后,SQL%Found值变化如下:true:Insert\Delete\Update\Select into至少返回一行。


     SQL%NotFound:Fetch数据没有返回数据行时,返回True,否则返回False。 


     SQL%RowCount:没有执行任何DML语句之前,值为null;对于 SELECT INTO语句 执行成功返回1;Insert into 时返回1;没有成功返回0,同时产生一个no_data_found异常。


6.游标For循环和嵌套循环


    游标For循环,游标打开、检索、关闭的过程被隐含地实现了。



DECLARE
  CURSOR c_zip IS SELECT * FROM zipcode; --定义游标
  vr_zip c_zip%ROWTYPE;--定义基于游标的记录变量
BEGIN
      FOR vr_zip IN c_zip LOOP  --游标For循环,游标的打开、检索、关闭的过程被隐含的实现
            dbms_output.put_line(vr_zip.zip||' '||vr_zip.city||'  '||vr_zip.state);
      END LOOP;
END;

嵌套游标示例:

--嵌套游标
DECLARE
  v_Section_id SECTION.SECTION_ID%TYPE;
  v_num NUMBER(10);
  CURSOR c_section_course IS  --父游标 收集某课程每个班级的信息
      SELECT b.description,a.section_id FROM SECTION a,course b WHERE a.course_no=b.course_no;
  CURSOR c_num IS   --子游标 统计每个section_id的注册数量
      SELECT c.section_id,COUNT(c.student_id) num FROM SECTION a,course b,enrollment c
      WHERE a.course_no=b.course_no AND a.section_id=c.section_id  AND c.section_id=v_Section_id
      GROUP BY c.section_id;
BEGIN
      FOR r_section_course IN c_section_course LOOP
            v_section_id:=r_section_course.section_id;
            v_num:=0;
            FOR r_num IN c_num LOOP
                  v_num:=v_num+NVL(r_num.num,0);
            END LOOP;
        dbms_output.put_line(r_section_course.section_id||' ,'||r_section_course.description||' 数量'||v_num);
      END LOOP;
END;

7.注意事项


   7.1 游标Select列表,此列表与PL/SQL变量或者PL/SQL记录组件进行匹配。

   7.2 相同数据类型的游标,Oracle支持赋值,即多个游标变量指向同一共享区域,共享指向位置实体。

DECLARE
   l_cv1    SYS_REFCURSOR;
   l_cv2    SYS_REFCURSOR;
   l_cv3    SYS_REFCURSOR;
   l_name   user_objects.object_name%TYPE;
BEGIN
   OPEN l_cv1 FOR SELECT ename FROM emp ORDER BY empno;

   FETCH l_cv1 INTO l_name;      --游标指向 SMITH 取出该值
   DBMS_OUTPUT.put_line (l_name);

   l_cv2 := l_cv1;     --L_cv2 是 L_cv1别名

   FETCH l_cv2 INTO l_name;     --游标继续指向下一个数值 ALLEN
   DBMS_OUTPUT.put_line (l_name);

   l_cv3:=l_cv2;        --L_cv3 是l_cv1别名
   FETCH l_cv3 INTO l_name;
   dbms_output.put_line(l_name);

   CLOSE l_cv1;--游标cv1关闭了, cv2|cv3也关闭了

   FETCH l_cv2 INTO l_name;
   DBMS_OUTPUT.put_line (l_name);

       FETCH l_cv3 INTO l_name;
       dbms_output.put_line(l_name);

   CLOSE l_cv2;
EXCEPTION
   WHEN OTHERS
   THEN
      DBMS_OUTPUT.put_line ('......ERROR.....');
END;




8.高级游标



   8.1 游标和嵌套游标中使用参数



       注意:游标参数可以有默认值;参数传递模型只能为IN



       示例:   



DECLARE
  CURSOR c_zip(p_state IN zipcode.state%TYPE DEFAULT 'CT') --带游标参数
          IS SELECT zip,city,state FROM zipcode WHERE state=p_state;
BEGIN
      FOR r_zip IN c_zip('NJ') LOOP
      dbms_output.put_line(r_zip.zip||' '||r_zip.city||' '||r_zip.state);
      END LOOP;
                   FOR r_zip IN c_zip() LOOP --此时为'CT'
      dbms_output.put_line(r_zip.zip||' '||r_zip.city||' '||r_zip.state);
      END LOOP;
END;





    8.2 FOR UPDATE和Where CURRENT游标



      使用游标更新数据库表时,需要使用FOR UPDATE子句。目的:锁定要更新的表,防止其他人在执行完更新操作和释放该锁之前进行更新操作。



      语法: 游标声明  for update ;  游标声明  for update of <item_name>;



             前者可以锁定到表多个数据项;后者可以锁定到表中成员某数据项。



      示例:   



declare
  cursor c_course is select course_no,cost from course for update; 
begin
      for r_course in c_course loop
            if r_course.cost<2500 then
                  update course
                   set cost=r_course.cost+10
                   where course_no=r_course.COURSE_NO;
        end if;
      
      end loop;
 commit;
end;



当希望更新最新检索的数据行时,可以使用for current of,只能在for update of 游标中使用 where current of 子句,好处是免于在update子句中使用where子句。