目录
1.游标
2.游标的声明
(1)显式游标声明
①定义游标之后打开游标
②FETCH读取数据?
(2)隐式游标声明
3.游标FOR循环
学习Oracle数据库相关基本操作(一)
学习Oracle数据库的新建表的基本操作(二)
学习Oracle数据库新建数据库操作(三)
学习Oracle数据库并对数据进行查询,插入等操作(四)
关于Oracle中的关闭和启动数据库的几种方式(五)
Oracle中含替换变量的查询(二)
Oracle中数据库的查询(三)
PL/SQL基础知识点(一)
PL/SQL变量,常量和数据类型(二)
PL/SQL基本程序结构和语句(三)
Oracle中系统内置函数(四)
Oracle中用户自定义函数(五)
1.游标
为什么需要游标呢?
我们之前使用SQL的语句通常查询数据库中的数据的时候会产生一组的结果集,然而这种结果是不能看成是一个单元来处理的,所以应用需要一种机制来保证每次处理结果集中的一行或者几行,那么游标就提供了这样的机制,可以对结果集进行逐行的处理(与某个查询结果相联系,可以指向结果集的任意位置,方便对指定位置的数据进行操作)。
游标包含:显式游标和隐式游标。
2.游标的声明
(1)显式游标声明
DECLARE CURSOR <游标名>
? ? ? ? IS
? ? ? ? <SELECT 语句>
使用游标之前首先需要打开游标:
OPEN <游标名>
①定义游标之后打开游标
当打开游标之后,可以使用系统变量%ROWCOUNT返回最后一次提取到数据行的序列号。打开游标之后,且提取数据之前可访问%ROWCOUNT值,返回值为0(第一次访问,那么提取到的数据行为0)。
DECLARE CURSOR XSB_CUR
? ? ? ? IS
? ? ? ? SELECT SID,SNAME
? ? ? ? ? ? ? ? FROM XSB;
? ? ? ? BEGIN?
? ? ? ? ? ? ? ? OPEN XSB_CUR;
? ? ? ? ? ? ? ? DBMS_OUTPUT.PUT_LINE(XSB%ROWCOUNT);
????????END;
②FETCH读取数据?
FETCH <游标名> [ INTO <变量名>,...n]
提示:这里的INTO主要将从当前游标所指向的位置读取的数据赋值给后面的变量(和前面所讲的 SELECT SID INTO <变量名>差不多)。
?关于FETCH读取数据,FETCH每一次读取一行的数据,然后自动将游标指针移动指向下一个数据行,当检索到最后一行数据时,再一次执行FETCH读取数据的话,那么会操作失败,并将游标%NOTFOUND设置为TRUE;
例子1:从学生表XSB中使用游标读取学生的学号,姓名和学分
SET SERVEROUTPUT ON;
DECLARE
--声明变量
ID XSB.sid%TYPE;
NAME XSB.sname%TYPE;
grade XSB.score%TYPE;
--声明游标
CURSOR XSB_CUR
IS
SELECT sid,sname,score
FROM XSB;
BEGIN
--首先打开游标
OPEN XSB_CUR;
--读取数据
FETCH XSB_CUR INTO ID,NAME,grade;
WHILE XSB_CUR%FOUND
LOOP
DBMS_OUTPUT.PUT_LINE('学号: '||to_char(ID)||' 姓名: '||to_char(NAME)||' 学分: '||to_char(grade));
--读取数据
FETCH XSB_CUR INTO ID,NAME,grade;
END LOOP;
--关闭游标
CLOSE XSB_CUR;
END;
注:如果试图打开一个已经打开的游标或者关闭一个已经关闭了的游标,都会出错。因此可以采用下面的语句首先进行判断游标的状态:
IF mycur%ISOPEN THEN
? ? ? ? FETCH mycur INTO [<变量名>,...n];
ELSE
? ? ? ? OPEN mycur;?
END
(2)隐式游标声明
如果在PL/SQL程序段中使用DML(DELETE,UPDATE,INSERT)或者SELECT语句进行操作的话,PL/SQL会隐含地处理游标定义。
例子:使用隐式游标的方式查询学号为‘151101’学生的信息:
SET SERVEROUTPUT ON; DECLARE? ? ? ID XSB.sid%TYPE; ? ? NAME XSB.sname%TYPE; ? ? GRADE XSB.score%TYPE; BEGIN ? ? SELECT sid,sname,score into ID,NAME,GRADE ? ? ? ? FROM XSB ? ? ? ? where sid='151101'; ? ? --这里使用隐式游标判断是否查询成功 ? ? IF SQL%FOUND THEN ? ? ? ? DBMS_OUTPUT.PUT_LINE('查询成功: '||' ?学号: '||to_char(ID)||' ?姓名: '||to_char(NAME)||' ?学分: '||to_char(GRADE)); ? ? END IF; END;
隐式游标注意的点:
(1)每一个隐式游标必须有一个INTO(当使用SELECT语句的时候);
(2)与显式游标一样,接收数据目标变量的数目,数据类型要SELECT查询的数据类型相同;
(3)隐式游标一次仅返回一行的数据,使用的使用必须检查异常(关于异常前面已经讲述);
(4)为确保隐式游标仅返回一行数据,可以使用ROWNUM=1来限定,表示返回一行数据。
3.游标FOR循环
其实前面在讲述显式游标的时候,举的那个例子是使用WHILE-LOOP-END循环来使用,也可以转换为FOR循环。
FOR<记录变量名> IN <游标名> [(<参数1>,[,<参数2>]...)] LOOP
? ? ? ? 语句段
END LOOP;
例子1:从学生表XSB中使用游标读取学生的学号,姓名和学分。
SET SERVEROUTPUT ON; DECLARE? ? ? --声明变量 ? ? ID XSB.sid%TYPE; ? ? NAME XSB.sname%TYPE; ? ? grade XSB.score%TYPE; ? ? --声明游标 ? ? CURSOR XSB_CUR ? ? IS ? ? SELECT sid,sname,score ? ? ? ? FROM XSB; ? ? BEGIN ? ? ? ? FOR curValue IN XSB_CUR LOOP ? ? ? ? ? ? ID:=curValue.sid; ? ? ? ? ? ? NAME:=curValue.sname; ? ? ? ? ? ? grade:=curValue.score; ? ? ? ? ? ? DBMS_OUTPUT.PUT_LINE('学号: '||to_char(ID)||' 姓名: '||to_char(NAME)||' 学分: '||to_char(grade)); ? ? ? ? END LOOP; ? ? END;
例子2:用PL/SQL定义游标:从雇员表中显示工资大于1000的记录,只要姓名、部门编号和工资并且删除编号为奇数的员工:
--select emp.ename,emp.deptno,emp.sal
-- from emp
-- where sal>1000 and ROWNUM=1;
SET SERVEROUTPUT ON;
--声明变量
DECLARE
v_sal number;
v_ename char(20);
v_dept int;
k int;
--定义游标
cursor csalary
IS
select ename,deptno,sal
from emp
where sal>1000;
BEGIN
--使用for循环游标
FOR valText IN csalary loop
v_ename:=valText.ename;
v_dept:=valText.deptno;
v_sal:=valText.sal;
k:=csalary%rowcount;
--对于奇数的打印和输出
if mod(k,2)=1 and csalary%found then
DBMS_OUTPUT.PUT_LINE('v_name: '||to_char(v_ename)||' v_dept: '||
to_char(v_dept)||'v_sal: '||to_char(v_sal));
end if;
end loop;
end;
例子3:游标的应用,编写PL/SQL块,将员工表EMP中的部门编号为10的所有员工删除,统计删除的人数及删除人的平均工资。
SET SERVEROUTPUT ON;
DECLARE
v_salNUM number:=0;
BEGIN
select avg(sal) into v_salNUM
from emp
where deptno=10;
delete
from emp
where deptno=10;
if sql%found then
DBMS_OUTPUT.PUT_LINE('删除的人数: '||to_char(sql%rowcount));
DBMS_OUTPUT.PUT_LINE('删除人的平均工资数: '||to_char(v_salNUM));
end if;
end;
rollback;
?
提示:可以看到当使用FOR循环的时候就不需要使用OPEN和CLOSE语句,并且程序的结构上看起来更加的清晰和容易理解。
|