下面的内容主要来自《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子句。