Oracle笔记:游标

01 概述

不论是Oracle或是SQL Server,只要是关系型数据库,都不可避免地会涉及游标的使用。所谓游标是指一种能从包括多条数据记录的结果集中每次提取一条记录的机制。简单地说,游标提供了一种在服务器内部处理结果集的方法,它可以识别一个数据集合内部指定的工作行,从而可以有选择地按行采取操作。游标是映射在结果集中一行数据上的位置实体,有了游标,用户就可以访问结果集中的任意一行数据了,将游标放置到某行后,即可对该行数据进行操作,如提取当前行的数据等。

02 oracle声明游标及使用的简单格式

DECLARE
C1 TABLENAME.COLUMN1%TYPE;
C2 TABLENAME.COLUMN2%TYPE;
CURSOR CURSORNAME--游标名 
IS
SELECT COLUMN1,COLUMN2 FROM TABLENAME;--将查询结果集和游标关联起来;声明游标必须指明目标对象,即指向对表或视图的查询结果集。

BEGIN
	OPEN CURSORNAME;--打开游标
	FETCH CURSORNAME INTO C1,C2;--从游标中提取数据;意思就是把游标当前指向的值赋给变量,然后再向下移动一行,一般结合for循环使用
	CLOSE CURSORNAME;--关闭游标;游标相当于指针变量,占用资源,使用完后需要关闭游标释放资源。
END;

03 在循环中使用游标

DECLARE
C_NO STU.NO%TYPE;
C_NAME STU.NAME%TYPE;
CURSOR CURSOR_STU
IS
SELECT NO,NAME FROM STU;

BEGIN
	OPEN CURSOR_STU;
	LOOP
		FETCH CURSOR_STU INTO C_NO,C_NAME;
		EXIT WHEN CURSOR_STU%NOTFOUND;
		DBMS_OUTPUT.PUT_LINE('NO: ' || C_NO || '; NAME: ' || C_NAME);
	END LOOP;

	CLOSE CURSOR_STU;
END;

04 使用游标提取整行数据

DECLARE
STU_ROW STU%ROWTYPE;
CURSOR CURSOR_STU
IS
SELECT * FROM STU;

BEGIN
	OPEN CURSOR_STU;
	LOOP
		FETCH CURSOR_STU INTO STU_ROW;
		EXIT WHEN CURSOR_STU%NOTFOUND;
		DBMS_OUTPUT.PUT_LINE('NO: ' || STU_ROW.NO || '; NAME: ' || STU_ROW.NAME);
	END LOOP;

	CLOSE CURSOR_STU;
END;

05 使用带参数游标

DECLARE
STU_ROW STU%ROWTYPE;
CURSOR CURSOR_STU(IN_NO VARCHAR2)
IS
SELECT * FROM STU WHERE NO = IN_NO;

BEGIN
	OPEN CURSOR_STU('120001');
	LOOP
		FETCH CURSOR_STU INTO STU_ROW;
		EXIT WHEN CURSOR_STU%NOTFOUND;
		DBMS_OUTPUT.PUT_LINE('NO: ' || STU_ROW.NO || '; NAME: ' || STU_ROW.NAME);
	END LOOP;

	CLOSE CURSOR_STU;
END;

06 使用默认参数游标

DECLARE
STU_ROW STU%ROWTYPE;
CURSOR CURSOR_STU(IN_NO VARCHAR2:='120001')
IS
SELECT * FROM STU WHERE NO = IN_NO;

BEGIN
	OPEN CURSOR_STU;
	LOOP
		FETCH CURSOR_STU INTO STU_ROW;
		EXIT WHEN CURSOR_STU%NOTFOUND;
		DBMS_OUTPUT.PUT_LINE('NO: ' || STU_ROW.NO || '; NAME: ' || STU_ROW.NAME);
	END LOOP;

	CLOSE CURSOR_STU;
END;

07 使用游标FOR循环

从前面的举例子中,在oracle PL/SQL语句块中使用游标都遵循下面的步骤:
1.声明游标;
2.打开游标;
3.从游标中取值;
4.检查哪一行被返回;
5.处理;
6.关闭循环;
7.关闭游标。
使用游标FOR循环的优点在于不需要显示打开游标,关闭游标,提取数据以及定义存放数据的变量等操作。

DECLARE
STU_ROW STU%ROWTYPE;
CURSOR CURSOR_STU
IS
SELECT * FROM STU;

BEGIN
	FOR STU_ROW IN CURSOR_STU
	LOOP
		DBMS_OUTPUT.PUT_LINE('NO: ' || STU_ROW.NO || '; NAME: ' || STU_ROW.NAME);
	END LOOP;
END;

08 使用游标FOR循环也可以用子查询实现

DECLARE
STU_ROW STU%ROWTYPE;

BEGIN
	FOR STU_ROW IN (SELECT * FROM STU)
	LOOP
		DBMS_OUTPUT.PUT_LINE('NO: ' || STU_ROW.NO || '; NAME: ' || STU_ROW.NAME);
	END LOOP;
END;

09 使用游标更新数据

DECLARE
STU_ROW STU%ROWTYPE;
CURSOR CURSOR_STU
IS
SELECT * FROM STU FOR UPDATE;

BEGIN
	OPEN CURSOR_STU;
	LOOP
		FETCH CURSOR_STU INTO STU_ROW;
		EXIT WHEN CURSOR_STU%NOTFOUND;
		UPDATE STU SET AGE =AGE + 1 WHERE CURRENT OF CURSOR_STU;
	END LOOP;
	CLOSE CURSOR_STU;
	COMMIT;
END;

如要使用游标更新数据,在声明游标时SELECT语句后必须要带有FOR UPDATE子句,用于在游标结果集数据上加行锁,以防止其他用户在相应行上执行更新或删除操作。在提取了游标数据之后,为了更新当前游标所在的行数据,必须在UPDATE语句中引用WHERE CURRENT OF 游标名 子句。
本实例的执行流程是:打开游标后,游标指针指向STU表中第一行数据,将该行加上共享锁,并将其AGE值加1,解除锁定;再将游标指针下移一行,重复该操作,直到游标内所有数据行都操作完成则关闭游标。

10 使用游标删除数据

DECLARE
STU_ROW STU%ROWTYPE;
CURSOR CURSOR_STU
IS
SELECT * FROM STU FOR UPDATE;

BEGIN
	OPEN CURSOR_STU;
	LOOP
		FETCH CURSOR_STU INTO STU_ROW;
		EXIT WHEN CURSOR_STU%NOTFOUND;
		IF STU_ROW.NO = '120010' THEN
			DELETE STU WHERE CURRENT OF CURSOR_STU;
		END IF;
	END LOOP;
	CLOSE CURSOR_STU;
	COMMIT;
END;

11 判断游标是否打开,获取记录行数

DECLARE
STU_ROW STU%ROWTYPE;
CURSOR CURSOR_STU
IS
SELECT * FROM STU FOR UPDATE;

BEGIN
	OPEN CURSOR_STU;
	IF CURSOR_STU%ISOPEN THEN--判断游标是否打开
		DBMS_OUTPUT.PUT_LINE('CURSOR IS OPEN');
	END IF;
	LOOP
		FETCH CURSOR_STU INTO STU_ROW;
		EXIT WHEN CURSOR_STU%NOTFOUND;
	END LOOP;
	DBMS_OUTPUT.PUT_LINE(CURSOR_STU%ROWCOUNT);--统计记录行
	CLOSE CURSOR_STU;
	COMMIT;
END;

12 游标在存储过程中做out参数的使用

CREATE OR REPLACE PACKAGE PKG_1
IS
	TYPE pkg_cur IS REF CURSOR; 
	PROCEDURE QUERY_STU(IN_NO IN VARCHAR2, CUR_STU OUT pkg_cur);
END PKG_1;


CREATE OR REPLACE PACKAGE BODY PKG_1
IS
	PROCEDURE QUERY_STU(IN_NO IN VARCHAR2, CUR_STU OUT pkg_cur)
	IS
	BEGIN
	     OPEN CUR_STU FOR
         SELECT NO,NAME FROM STU WHERE NO = IN_NO;
	EXCEPTION 
		WHEN OTHERS THEN
		RAISE;
	END;
END PKG_1;

测试如图,一般情况动态游标应该返回给应用程序使用
在这里插入图片描述
查看打开的游标

select * from v$open_cursor;
select * from v$sysstat where name = 'opened_cursors_current';

在这里插入图片描述

others

通过以上语句格式声明的游标称为显示游标。游标与变量一样在使用之前需要预先声明。但是,与变量不同的是,在使用游标之前还必须打开游标。在Oracle中,打开游标的操作目的是激活游标声明中的查询语句并识别活动数据集。游标被关闭后,其中所取的数据都被释放,事实上,从游标被打开后,直到关闭之前,取回到活动集的所有数据都是静态的,或者说,游标忽略所有在游标打开之后对数据执行的SQL DML命令(INSERT,UPDATE,DELETE和SELECT),也就是说游标打开之后,查询中的表数据发生改变,游标中的数据不会随之改变因此只有在需要时才打开它,要刷新活动集,只需要关闭并重新打开游标即可。循环条件中CURSOR_STU%NOTFOUND表示游标指针的取值为空,如果需要表示指针值不为空可以使用CURSOR_STU%FOUND表示。与PL/SQL中的存储过程和函数相似,Oracle允许用户将参数传递给游标并在查询中使用,这对于处理在某种条件下打开游标的情况时非常有用。与存储过程不同的是,带参数的游标只能接受传递的值,而不能返回值。

TYPE pkg_cur IS REF CURSOR; 创建一个类型变量pkg_cur,它引用游标。
这种变量通常用于存储过程和函数返回结果集时使用,因为PL/SQL不允许存储过程或函数直接返回结果集,但可以返回类型变量,于是引用游标的类型变量作为输出参数或返回值就应运而生了。
静态游标和REF游标的区别是什么?
1.静态游标是静态定义,REF游标是动态关联;
2.使用REF游标需REF游标变量;
3.REF游标能做为参数进行传递,而静态游标是不可能的。

DROP TABLE STU
CREATE TABLE STU
(
  NO      VARCHAR2(10 BYTE),
  NAME    VARCHAR2(10 BYTE),
  GENTLE  VARCHAR2(2 BYTE),
  AGE     NUMBER(2),
  DEPT    VARCHAR2(20 BYTE)
);

SET DEFINE OFF;
Insert into STU
   (NO, NAME, GENTLE, AGE, DEPT)
 Values
   ('120006', '李飒', '男', 12, '12工商管理');
Insert into STU
   (NO, NAME, GENTLE, AGE, DEPT)
 Values
   ('120005', '林琳', '女', 22, '12计算机');
Insert into STU
   (NO, NAME, GENTLE, AGE, DEPT)
 Values
   ('120004', '杨过', '男', 22, '12计算机');
Insert into STU
   (NO, NAME, GENTLE, AGE, DEPT)
 Values
   ('120003', '张清', '女', 21, '12外语');
Insert into STU
   (NO, NAME, GENTLE, AGE, DEPT)
 Values
   ('120001', '陈诚', '男', 23, '12计算机');
Insert into STU
   (NO, NAME, GENTLE, AGE, DEPT)
 Values
   ('120002', '李宗赫', '男', 25, '12图形');
COMMIT;

猜你喜欢

转载自blog.csdn.net/qq_39827640/article/details/106625911