Oracle Lesson 6 游标管理

来源:互联网 发布:javascript !== 编辑:程序博客网 时间:2024/05/01 09:44
 
Oracle Lesson 6 游标管理
Made by Zhangxu 2008.3.3
 
                                                              
游标简介
游标的类型
游标主要分为如下几种
如图:
select语句从数据库中返回的记录多与一条时,我们可以考虑使用游标来解决!!
隐式游标
PL/SQL中使用DML语句时系统自动创建隐式游标
隐式游标自动声明、打开和关闭,其名为 SQL
通过检查隐式游标的属性可以获得最近执行的DML 语句的信息
隐式游标的属性有:
         %FOUND – SQL 语句影响了一行或多行时为 TRUE
         %NOTFOUND – SQL 语句没有影响任何行时为TRUE
         %ROWCOUNT – SQL 语句影响的行数
         %ISOPEN - 游标是否打开,始终为FALSE
代码示例:
SQL> SET SERVEROUTPUT ON
SQL> BEGIN
    UPDATE toys SET toyprice=270
    WHERE toyid= 'P005';
    IF SQL%FOUND THEN
       DBMS_OUTPUT.PUT_LINE(‘表已更新');
    END IF;
     END;
    /
注:只有在DML语句影响一行或者多行时。才返回true
 
SQL> SET SERVEROUTPUT ON
SQL> DECLARE
  v_TOYID TOYS.ID%type := '&TOYID';
  v_TOYNAME TOYS.NAME%Type := '&TOYNAME';
     BEGIN
  UPDATE TOYS SET NAME = v_TOYNAME
  WHERE toyid=v_TOYID;
  IF SQL%NOTFOUND THEN
           DBMS_OUTPUT.PUT_LINE('编号未找到。');
    ELSE
       DBMS_OUTPUT.PUT_LINE(‘表已更新');
    END IF;
     END;
/
注:如果 DML 语句不影响任何行,则返回 True
SQL> SET SERVEROUTPUT ON
SQL> BEGIN
    UPDATE vendor_master
    SET venname= 'Rob Mathew'
    WHERE vencode='V004';
    DBMS_OUTPUT.PUT_LINE (SQL%ROWCOUNT);
     END;
     /
注:返回DML语句影响的行数
SELECT INTO 语句
1)注意:如果没有与SELECT INTO语句中的条件匹配的行,将引发NO_DATA_FOUND异常
代码示例:
SQL> SET SERVEROUTPUT ON
SQL> DECLARE
    empid VARCHAR2(10);
    desig VARCHAR2(10);
     BEGIN
    empid:= '&Employeeid';
    SELECT designation INTO desig
    FROM employee WHERE empno=empid;
     EXCEPTION
    WHEN NO_DATA_FOUND THEN   ---如果没有与SELECT INTO语句中的条件匹配的行,将引发NO_DATA_FOUND异常!
     DBMS_OUTPUT.PUT_LINE('职员未找到');
     END;
     /
2)注意:如果 SELECT INTO 语句返回多个值,
将引发TOO_MANY_ROWS异常
代码示例
SQL> SET SERVEROUTPUT ON
SQL> DECLARE
    empid VARCHAR2(10);
     BEGIN
    SELECT empno INTO empid FROM employee;
     EXCEPTION
    WHEN TOO_MANY_ROWS THEN--2)注意:如果 SELECT INTO 语句返回多个值,
将引发TOO_MANY_ROWS异常!!
     DBMS_OUTPUT.PUT_LINE('该查询提取多行');
     END;
     /
显示游标
显式游标在 PL/SQL 块的声明部分定义查询,该查询可以返回多行
显式游标的操作过程:
1.声明一些变量,用于保存SELECT语句返回的例的值!
2.声明游标,并指定SELECT语句
3.打开游标
4.从游标中提取数据
5.关闭游标
代码示例:
SQL>SET SERVEROUTPUT ON                       –-能看到输出的数据
SQL>DECLARE                                         声明
        my_toy_price toys.toyprice%TYPE;               CURSOR toy_cur IS
           SELECT toyprice FROM toys
           WHERE toyprice<250;
    BEGIN
         OPEN toy_cur;                               --打开游标
         LOOP
         FETCH toy_cur INTO my_toy_price;       --提取游标
         EXIT WHEN toy_cur%NOTFOUND;
         DBMS_OUTPUT.PUT_LINE
          ('TOYPRICE=:玩具单价='||my_toy_price);
         END LOOP;
         CLOSE toy_cur;                             --关闭游标
   END;
带参数的显示游标
SQL> SET SERVEROUTPUT ON
SQL> DECLARE
       desig    VARCHAR2(20);
       emp_code VARCHAR2(5);
       empnm    VARCHAR2(20);
       CURSOR emp_cur(desigparam VARCHAR2) IS
        SELECT empno, ename FROM employee
        WHERE designation=desig;
     BEGIN
       desig:= '&desig';
       OPEN emp_cur(desig);
       LOOP
           FETCH emp_cur INTO emp_code,empnm;
           EXIT WHEN emp_cur%NOTFOUND;
           DBMS_OUTPUT.PUT_LINE(emp_code||' '||empnm);
       END LOOP;
       CLOSE emp_cur;
     END;
使用显式游标更新行
允许使用游标删除或更新活动集中的行
声明游标时必须使用 SELECT … FOR UPDATE语句
   
    CURSOR <cursor_name> IS
     SELECT statement FOR UPDATE;
 
    UPDATE <table_name>
    SET <set_clause>
    WHERE CURRENT OF <cursor_name>
代码示例:
SQL> SET SERVEROUTPUT ON
SQL> DECLARE
 new_price NUMBER;
 CURSOR cur_toy IS
    SELECT toyprice FROM toys WHERE toyprice<100
    FOR UPDATE OF toyprice;
BEGIN
 OPEN cur_toy;
 LOOP
    FETCH cur_toy INTO new_price;
    EXIT WHEN cur_toy%NOTFOUND;
    UPDATE toys
    SET toyprice = 1.1*new_price
    WHERE CURRENT OF cur_toy;
 END LOOP;
 CLOSE cur_toy;
 COMMIT;
END;
 
循环游标
循环游标用于简化游标处理代码
当用户需要从游标中提取所有记录时使用
循环游标的语法如下:
FOR <record_index> IN <cursor_name>
LOOP
    <executable statements>
END LOOP;
代码示例:
SQL> SET SERVER OUTPUT ON
SQL> DECLARE
     CURSOR mytoy_cur IS
        SELECT toyid, toyname, toyprice
        FROM toys;
     BEGIN
     FOR toy_rec IN mytoy_cur
     LOOP
            DBMS_OUTPUT.PUT_LINE(
                             ‘玩具编号:'||' ' ||toy_rec.toyid||' '           
            ||‘玩具名称:'||' '||toy_rec.toyname||' '
            ||‘玩具单价:'||' '||toy_rec.toyprice);
     END LOOP;
     END;
 
REF 游标和游标变量
REF 游标用于处理运行时才能确定的动态 SQL 查询的结果
         REF 游标和游标变量用于处理运行时动态执行的 SQL 查询
         创建游标变量需要两个步骤:
         声明 REF 游标类型
         声明 REF 游标类型的变量
         用于声明 REF 游标类型的语法为:
 
       TYPE <ref_cursor_name> IS REF CURSOR
       [RETURN <return_type>];
打开游标变量的语法如下:
        OPEN cursor_name FOR select_statement;
声明强类型的 REF 游标
TYPE my_curtype IS REF CURSOR
 RETURN stud_det%ROWTYPE;
order_cur my_curtype;
声明弱类型的 REF 游标
TYPE my_ctype IS REF CURSOR;
stud_cur my_ctype;
 
示例:
SQL> DECLARE
     TYPE toys_curtype IS REF CURSOR—声明REF游标类型
          RETURN toys%ROWTYPE;
   Toys_curvar toys_curtype;--声明游标变量
     toys_rec toys%ROWTYPE;
     BEGIN
  OPEN toys_curvar FOR
    SELECT * FROM toys;
  FETCH toys_curvar INTO toys_rec;
       ...
  CLOSE toys_curvar;
     END;
 
游标变量的优点和限制
游标变量的优点有:
可从不同的 SELECT 语句中提取结果集
可以作为过程的参数进行传递
可以引用游标的所有属性
可以进行赋值运算
使用游标变量的限制:
不能在程序包中声明游标变量
FOR UPDATE子句不能与游标变量一起使用
不能使用比较运算符
使用游标变量执行动态 SQL
DECLARE
 r_emp emp%ROWTYPE;
 TYPE c_type IS REF CURSOR;
 cur c_type;
 p_salary NUMBER;
BEGIN
 p_salary := 2500;
 OPEN cur FOR 'select * from emp where sal>:1
                order by sal desc'
 USING p_salary;
 DBMS_OUTPUT.PUT_LINE('薪水大于'|| p_salary ||'的员工有:');
 LOOP
    FETCH cur INTO r_emp;
    EXIT WHEN cur%NOTFOUND;
    DBMS_OUTPUT.PUT_LINE('编号:'|| r_emp.empno
      || ' 姓名:' || r_emp.ename|| ' 薪水:' || r_emp.sal );
 END LOOP;
 CLOSE cur;
END;
 
总结
游标用于处理查询结果集中的数据
游标类型有:隐式游标、显式游标和 REF 游标
隐式游标由 PL/SQL 自动定义、打开和关闭
显式游标用于处理返回多行的查询
显式游标可以删除和更新活动集中的行
要处理结果集中所有记录时,可使用循环游标
在声明 REF 游标时,不需要将 SELECT 语句与 其关联
附录:部分源码
---------------------------隐式游标:-------------------------------------------------------------------
---隐式游标的名称为SQL。不能为sql游标显示执行open,fetch,close语句
---所有操作都是隐式完成
declare
v_rows number;
begin
update student set name='roses' where id=2;
v_rows:=SQL%rowcount;
dbms_output.put_line('把所有编号为2的学生名字修改为roses');
end;
 
---隐式游标的属性
--SQL%ROWCOUNT 返回的行数只为1 修改影响的行数
--SQL%NOTFOUND 修改没有结果
-------------------------------------------------------------
--------------游标的使用--------------------------------------
declare
type sturecord_type is record
(
id student.id%type,
name student.name%type,
age student.age%type
);
sturecord sturecord_type;
name student.name%type;
age student.age%type;
---声明游标
cursor c1
is
select name,age from student where id=1;
cursor c2(stu_no int)--有参数的游标
is
select name,age from student where id=stu_no;
cursor c3(stu_no int) -- 有返回值的游标
return sturecord
is
select id,name,age from student where id=stu_no;
cursor c4(stu_no int)---基于游标定义记录变量
is
select name,age from student where id=stu_no;
sturecord_2 c4%rowtype;
begin
open c3(2);--打开游标
loop
fetch c3 into sturecord;--提取游标
if c3%found then
dbms_output.put_line(sturecord.name);
else
exit;
end if;
 
end loop;
end;
-------------------------------------------------------------
------------------显式游标属性--------------------------------
cursor_name%isopen 如果游标打开为真否则为假
cursor_name%found 如果最近一次提取游标操作fetch成功则为真,否则为假
在游标打开之后,提取之前访问%foundnull,当游标打开之前访问%found将引起异常
 
cursor_name%notfound 如果最近一次提取游标操作fetch失败为true,否则为false;
 
cursor_name%rowcount 返回最近一次提取到数据行的序号
-------------------------------------------------------------
-------------使用游标更新数据----------------------------------
declare
stu_rec student%rowtype;
cursor c1
is
select * from student for update;
begin
open c1;
loop
fetch c1 into stu_rec;
exit when c1%notfound;
if stu_rec.age>=30 then
update student set id=10 where current of c1;
end if;
end loop;
commit;
close c1;
end;
-----------------------------------------------------------------------------游标for循环-----------------------------------
declare
type sturecord_type is record
(
id student.id%type,
name student.name%type,
age student.age%type
);
sturecord sturecord_type;
cursor c1(num_no int default 1)
is
select * from student where id=num_no;
begin
dbms_output.put_line('ID传递参数为2');
for sturecord in c1(2) loop
dbms_output.put_line(sturecord.name);
end loop;
end;
------------------------------------------------------------------------作用域---------------------------------------------
<<block>>
declare
v_1 varchar2(20):='global v1';
begin
dbms_output.put_line(v_1);
<<subblock_1>>
declare
v_1 varchar2(20):='subblock_1 local v1';
begin
dbms_output.put_line(block.v_1);
dbms_output.put_line(v_1);
end;
end;
-------------------------------------------------------------
--------------------record-----------------------------------
declare
type stu_record_type is record
(
id student.id%type,
name student.name%type,
age student.age%type
);
stu_record stu_record_type;
begin
select id,name,age into stu_record from student
where id=&id;
dbms_output.put_line('编号'||stu_record.id);
dbms_output.put_line('姓名'||stu_record.name);
dbms_output.put_line('年龄'||stu_record.age);
end;
-------------------------------------------------------------
原创粉丝点击