标签:
oracle plsql cursor usage
一、游标的相关概念及特性
1.定义
映射在结果集中某一行数据的具体位置,类似于C语言中的指针。即通过游标方式定位到结果集中某个特定的行,然后根据业务需求对该行进行相应特定的操作。
2.游标的分类
在Oracel中,游标可以分为两大类:静态游标 和 REF游标(动态游标)。REF游标是一种引用类型,类似于指针。而静态游标又分为显式游标和隐式游标两种。
3.游标使用的一般过程:
显式游标:声明--->打开--->读取--->关闭。
隐式游标:直接使用读取,声明、打开、关闭都是系统自动进行的。
4.游标的属性:
(1)公共的属性:%FOUND,%NOTFOUND,%ISOPEN,%ROWCOUNT。
%FOUND 布尔型属性,返回TRUE如果INSERT或UPDATE DELETE语句影响了一行或多行或SELECT INTO语句返回一行或多行;否则,返回FALSE。
%NOTFOUND 布尔型属性,与%FOUND相反。返回TRUE,如果INSERT或UPDATE DELETE语句没有影响任何行,或SELECT INTO语句没有返回任何行;否则,返回FALSE
%ISOPEN 布尔型属性,判断游标的状态,当游标已经打开时返回TRUE,游标关闭时则返回FALSE。隐式游标总是返回FALSE,因为系统在执行后自动关闭游标。
%ROWCOUNT 数字型属性,返回受SQL影响的行数。
注:当使用隐式游标的属性时,需要在属性前加上SQL。因为Oracle在创建隐式游标时,默认的游标名为SQL。比如:SQL%NOTFOUND;
(2)额外的属性:%BULK_ROWCOUNT,%BULK_EXCEPTIONS。
%BULK_ROWCOUNT被用于FORALL语句。此属性的第i个元素存储 FORALL LOOP中第 i个执行中的处理的行数UPDATE或DELETE语句。如果第 i个执行不会影响任何行%BULK_ROWCOUNT(i)返回零。
%BULK_EXCEPTIONS被用于FORALL语句。此属性存储FORALL
LOOP中第i个执行的异常,%BULK_EXCEPTIONS(i).ERROR_CODE对应错误代
码,%BULK_EXCEPTIONS(i).ERROR_INDEX对应错误信息。
注:
1.不能比较两个游标变量是否相等或者不等。(cv1 emps_rc; cv2 emps_rc; IF cv1 = cv2)
2.CURSOR 类型不能够用于COLLECTION。(TYPE cvs_t IS TABLE OF SYS_REFCURSOR;)
3.基于REF CURSOR的游标变量无法在PACKAGE的级别被定义,只能够定义在过程,函数,匿名块或触发器里面。(CREATE OR REPLACE PACKAGE plch_pkg IS g_cursor SYS_REFCURSOR; END;) 回到顶部
二、隐式游标
1.隐式游标定义
隐式游标由系统自动定义,其过程由oracle控制,完全自动化。比如当DML被使用时,Oracle为每一个不属于显式游标的DML语句都创建一个隐式
游标,其声明、打开、关闭都是系统自动进行。另外,隐式游标默认的名称是SQL,不能对SQL游标显式地执行OPEN,FETCH,CLOSE语句。
2.隐式游标的属性
类似于显式游标,隐式游标同样具有四种属性,只不过隐式游标以SQL%开头,而显示游标以Cursor_name%开头。
并且,通过SQL%总是只能访问前一个DML操作或单行SELECT操作的游标属性,用于判断DML执行的状态和结果,进而控制程序的流程。
SQL%ISOPEN
游标是否打开。当执行select into、insert、update、delete这些DML操作时,Oracle会隐含地打开游标,且在该语句执行完毕或隐含地关闭游标。
因为是隐式游标,故SQL%ISOPEN总是FALSE。
SQL%FOUND
判断SQL语句是否成功执行。当有作用行时则成功执行为TRUE,否则为FALSE。
SQL%NOTFOUND
判断SQL语句是否成功执行。当有作用行时否其值为FALSE,否则其值为TRUE。
SQL%ROWCOUNT
在执行任何DML语句之前,SQL%ROWCOUNT的值都是NULL。
对于SELECTINTO语句,如果执行成功,SQL%ROWCOUNT的值为1;
如果没有成功,SQL%ROWCOUNT的值为0,同时产生一个异常NO_DATA_FOUND。
3.SELECT INTO时使用隐式游标
SELECT INTO用于将单行结果集放置到变量之中。SELECT INTO处理的结果包括三种情况
查询结果返回单行,SELECT INTO被成功执行;
查询结果没有返回行,PL/SQL将抛出no_data_found异常;
查询结果返回多行,PL/SQL将抛出too_many_rows 异常。
对于上述两种异常发生时,类似于普通异常处理,程序控制权转移到异常处理部分(如没有异常处理则程序中断)。对于异常被激后发,SQL游标的四个属性在此将不可使用,如下面的例子:
1 DECLARE
2 v_ename emp.ename%TYPE;
3 BEGIN
4 SELECT ename INTO v_ename FROM emp WHERE empno=&no;
5 IF SQL%ROWCOUNT=0 OR SQL%NOTFOUND THEN
6 DBMS_OUTPUT.PUT_LINE(‘The record ‘||&no||‘ is not exist!‘);
7 ELSE
8 DBMS_OUTPUT.PUT_LINE(‘The name for record ‘||&no||‘ is ‘||v_ename );
9 END IF;
10 EXCEPTION
11 WHEN NO_DATA_FOUND THEN
12 DBMS_OUTPUT.PUT_LINE(‘No data found for ‘||&no);
13 END;
14 操作及结果:
15 Enter value for no:70
16 No data found for 70
17
18 Enter value for no:7788
19 The name for record 7788 is SCOTT
从上面的演示中可以看到,当select into没有返回行时,IF SQL%ROWCOUNT=0OR SQL%NOTFOUND THEN 语句并没有被执行。
使用下面改进过的代码来执行,即可以将SQL游标属性判断放置到EXCEPTION部分。
1 DECLARE
2 v_ename emp.ename%TYPE;
3 BEGIN
4 SELECT ename INTO v_ename FROM emp WHERE empno=&no;
5 IF SQL%NOTFOUND THEN
6 DBMS_OUTPUT.PUT_LINE(‘The record ‘||&no||‘ is not exist!‘);
7 ELSE
8 DBMS_OUTPUT.PUT_LINE(‘The name for record ‘||&no||‘ is ‘||v_ename );
9 END IF;
10 EXCEPTION
11 WHEN NO_DATA_FOUND THEN
12 IF SQL%NOTFOUND THEN
13 DBMS_OUTPUT.PUT_LINE(‘The record ‘||&no||‘ is not exist!‘);
14 DBMS_OUTPUT.PUT_LINE(‘No data found for ‘||&no);
15 ELSE
16 DBMS_OUTPUT.PUT_LINE(‘The name for record ‘||&no||‘ is ‘||v_ename );
17 END IF;
18 END;
19 操作及结果:
20 Enter value for no:80
21 The record 80 is not exist!
22 No data found for 80
三、显式游标
1.显式游标的属性
显式游标的也是4个属性(%FOUND,%NOTFOUND,%ISOPEN,%ROWCOUNT),与隐式游标的相同。
2.显式游标的使用步骤:声明(定义)--->打开--->读取--->关闭。
(1) 声明游标
格式如下:
1 CURSOR cursor_name [(parameter[, parameter]...)]
2 [RETURN return_type]
3 IS select_statement
4 例子:
5 DECLARE
6 CURSOR emp_cursor(department IN number2 DEFAULT 20)
7 return emp%rowtype
8 IS
9 SELECT *
10 FROM emp
11 WHERE deptno=department;
(2)打开游标
格式:OPEN cursor_name[(VALUE[,VALUE]...)];
例子:OPEN emp_cursor;
(3)读取游标数据
格式:FETCH cursor_name INTO { variable_list | record_variable };
例子:FETCH emp_cursor INTO emp_row;
注:游标使用的参数在执行中变化并不影响结果。如下例中:即使 factor一直在增加,但salary一直是乘2。
1 DECLARE
2 my_sal employees.salary%TYPE;
3 my_job employees.job_id%TYPE;
4 factor INTEGER := 2;
5 CURSOR c1 IS
6 SELECT factor*salary FROM employees WHERE job_id = my_job;
7 BEGIN
8 OPEN c1; -- factor initially equals 2
9 LOOP
10 FETCH c1 INTO my_sal;
11 EXIT WHEN c1%NOTFOUND;
12 factor := factor + 1; -- does not affect FETCH
13 END LOOP;
14 CLOSe c1;
15 END;
16 /
(4)关闭游标
格式:CLOSE cursor_name;
例子:CLOSE emp_cursor;
1 --下面的程序将显示声明一个游标,并在循环中使用SELECT语句提取所有部门为30的员工信息。
2 DECLARE
3 CURSOR emp_cursor(department IN NUMBER DEFAULT 20) IS
4 SELECT empno, ename, job, sal
5 FROM emp
6 WHERE deptno = department;
7
8 TYPE employee IS RECORD(
9 id emp.empno%TYPE,
10 NAME emp.ename%TYPE,
11 job emp.job%TYPE,
12 sal emp.sal%TYPE);
13 emp_row employee;
14 BEGIN
15 OPEN emp_cursor(30);
16 FETCH emp_cursor INTO emp_row;
17 WHILE emp_cursor%FOUND LOOP
18 dbms_output.put(‘员工编号 ‘ || emp_row.id);
19 dbms_output.put(‘ 姓名 ‘ || emp_row.name);
20 dbms_output.put(‘ 职位 ‘ || emp_row.job);
21 dbms_output.put(‘ 薪金 ‘ || emp_row.sal);
22 --填充下一条记录
23 FETCH emp_cursor INTO emp_row;
24 END LOOP;
25 CLOSE emp_cursor;
26 END;
在大多数时候我们在自定义显示游标的时候都遵循下面的步骤:
1、打开游标
2、开始循环
3、从游标中取值
4、检查那一行被返回
5、处理
6、关闭循环
7、关闭游标
可以简单的把这一类代码称为游标用于循环。但还有一种循环与这种类型不相同,这就是FOR循环,用于FOR循环的游标按照正常的声明方式声明,它的优点在
于不需要显式的打开、关闭、取数据,测试数据的存在、定义存放数据的变量等等。游标FOR 循环的语法如下:
1 FOR record_index in cursor_name
2 LOOP
3 {...statements...}
4 END LOOP;
5 使用例子:
6 CREATE OR REPLACE Function TotalIncome
7 ( name_in IN varchar2 )
8 RETURN varchar2
9 IS
10 total_val number(6);
11 cursor c1 is
12 SELECT monthly_income
13 FROM employees
14 WHERE name = name_in;
15 BEGIN
16 total_val := 0;
17 FOR employee_rec in c1
18 LOOP
19 total_val := total_val + employee_rec.monthly_income;
20 END LOOP;
21 RETURN total_val;
22 END;
五、SELECT FOR UPDATE
1.定义
SELECT FOR UPDATE 语句可以锁住游标结果集。直到下一次 COMMIT 或ROLLBACK 操作执行后,才解锁游标。
2.语法
1 CURSOR cursor_name
2 IS
3 select_statement
4 FOR UPDATE [OF column_list] [NOWAIT];
5
6 cursor_name: 游标名。
7 select_statement: SELECT 查询语句。
8 column_list: 游标结果集中想要更新的字段。
9 NOWAIT: 选择游标是否等待资源。
1 CURSOR c1
2 IS
3 SELECT course_number, instructor
4 FROM courses_tbl
5 FOR UPDATE OF instructor;
6 注:可以使用 WHERE CURRENT OF 语句 来更新或删除被 SELECT FOR UPDATE 锁住的数据。
1 UPDATE table_name
2 SET set_clause
3 WHERE CURRENT OF cursor_name;
4 或
5 DELETE FROM table_name
6 WHERE CURRENT OF cursor_name;
注: WHERE CURRENT OF 语句只更新或删除最后被游标 FETCH 到的记录。
2.使用例子
1 (1)Updating using the WHERE CURRENT OF Statement
2 CREATE OR REPLACE Function FindCourse
3 ( name_in IN varchar2 )
4 RETURN number
5 IS
6 cnumber number;
7 CURSOR c1
8 IS
9 SELECT course_number
10 FROM courses_tbl
11 WHERE course_name = name_in
12 FOR UPDATE of instructor;
13 BEGIN
14 OPEN c1;
15 FETCH c1 INTO cnumber;
16 if c1%notfound then
17 cnumber := 9999;
18 else
19 UPDATE courses_tbl
20 SET instructor = ‘SMITH‘
21 WHERE CURRENT OF c1;
22 COMMIT;
23 end if;
24 CLOSE c1;
25 RETURN cnumber;
26 END;
27
28 (2)Deleting using the WHERE CURRENT OF Statement
29 CREATE OR REPLACE Function FindCourse
30 ( name_in IN varchar2 )
31 RETURN number
32 IS
33 cnumber number;
34 CURSOR c1
35 IS
36 SELECT course_number
37 from courses_tbl
38 where course_name = name_in
39 FOR UPDATE of instructor;
40 BEGIN
41 open c1;
42 fetch c1 into cnumber;
43 if c1%notfound then
44 cnumber := 9999;
45 else
46 DELETE FROM courses_tbl
47 WHERE CURRENT OF c1;
48 COMMIT;
49 end if;
50 close c1;
51 RETURN cnumber;
52 END;
标签:
原文地址:http://www.cnblogs.com/ct-blog/p/5056949.html