[by 王谭]oracle游标的使用教程详解编程语言

oracle游标的使用教程


游标的概念:
游标是SQL的一个内存工作区,由系统或用户以变量的形式定义。游标的作用就是用于临时存储从数据库中提取的数据块。在某些情况下,需要把数据从存放在磁盘的表中调到计算机内存中进行处理,最后将处理结果显示出来或最终写回数据库。这样数据处理的速度才会提高,否则频繁的磁盘数据交换会降低效率。
游标有两种类型:显式游标和隐式游标。在前述程序中用到的SELECT…INTO…查询语句,一次只能从数据库中提取一行数据,对于这种形式的查询和DML操作,系统都会使用一个隐式游标。但是如果要提取多行数据,就要由程序员定义一个显式游标,并通过与游标有关的语句进行处理。显式游标对应一个返回结果为多行多列的SELECT语句。
游标一旦打开,数据就从数据库中传送到游标变量中,然后应用程序再从游标变量中分解出需要的数据,并进行处理。
隐式游标
如前所述,DML操作和单行SELECT语句会使用隐式游标,它们是:
* 插入操作:INSERT。
* 更新操作:UPDATE。
* 删除操作:DELETE。
* 单行查询操作:SELECT … INTO …。
当系统使用一个隐式游标时,可以通过隐式游标的属性来了解操作的状态和结果,进而控制程序的流程。隐式游标可以使用名字SQL来访问,但要注意,通过SQL游标名总是只能访问前一个DML操作或单行SELECT操作的游标属性。所以通常在刚刚执行完操作之后,立即使用SQL游标名来访问属性。游标的属性有四种,如下所示。

隐式游标的属性 返回值类型 意 义
SQL%ROWCOUNT 整型 代表DML语句成功执行的数据行数
SQL%FOUND 布尔型 值为TRUE代表插入、删除、更新或单行查询操作成功
SQL%NOTFOUND 布尔型 与SQL%FOUND属性返回值相反
SQL%ISOPEN 布尔型 DML执行过程中为真,结束后为假

–以下建表、插入数据、游标测试代码在plsqldeveloper内创建sql窗口直接运行即可
先把以下demo内的代码运行环境配置下吧!
我都测试过了,嘎嘎

create table  emp( 
name  varchar2(20), 
sex varchar2(20), 
dept varchar2(20) 
); 
insert into  emp values('tom','男','IT'); 
insert into  emp values('jarry','女','sell'); 
insert into  emp values('jason','男','manager');

举个栗子!使用隐式游标的属性,判断对员工性别描述的修改是否成功。
输入和运行以下程序:

     BEGIN   
        UPDATE emp SET sex=sex||'生' WHERE sex='女';    
         IF SQL%FOUND THEN     
        DBMS_OUTPUT.PUT_LINE('成功修改性别描述!');    
        COMMIT;     
        ELSE   
        DBMS_OUTPUT.PUT_LINE('修改性别描述失败!');    
         END IF;     
        END;  

显式游标
游标的定义和操作
游标的使用分成以下4个步骤。
1.声明游标
在DECLEAR部分按以下格式声明游标:
CURSOR 游标名[(参数1 数据类型[,参数2 数据类型…])]
IS SELECT语句;
参数是可选部分,所定义的参数可以出现在SELECT语句的WHERE子句中。如果定义了参数,则必须在打开游标时传递相应的实际参数。
SELECT语句是对表或视图的查询语句,甚至也可以是联合查询。可以带WHERE条件、ORDER BY或GROUP BY等子句,但不能使用INTO子句。在SELECT语句中可以使用在定义游标之前定义的变量。
2.打开游标
在可执行部分,按以下格式打开游标:
OPEN 游标名[(实际参数1[,实际参数2…])];
打开游标时,SELECT语句的查询结果就被传送到了游标工作区。
3.提取数据
在可执行部分,按以下格式将游标工作区中的数据取到变量中。提取操作必须在打开游标之后进行。
FETCH 游标名 INTO 变量名1[,变量名2…];

FETCH 游标名 INTO 记录变量;
游标打开后有一个指针指向数据区,FETCH语句一次返回指针所指的一行数据,要返回多行需重复执行,可以使用循环语句来实现。控制循环可以通过判断游标的属性来进行。
下面对这两种格式进行说明:
第一种格式中的变量名是用来从游标中接收数据的变量,需要事先定义。变量的个数和类型应与SELECT语句中的字段变量的个数和类型一致。
第二种格式一次将一行数据取到记录变量中,需要使用%ROWTYPE事先定义记录变量,这种形式使用起来比较方便,不必分别定义和使用多个变量。
定义记录变量的方法如下:
变量名 表名|游标名%ROWTYPE;
其中的表必须存在,游标名也必须先定义。
4.关闭游标
CLOSE 游标名;
显式游标打开后,必须显式地关闭。游标一旦关闭,游标占用的资源就被释放,游标变成无效,必须重新打开才能使用。
以下是使用显式游标的一个简单练习。

再举一堆栗子!使用显式游标:
for循环中游标的使用 与 其他不同,在for循环中使用游标的时候:不用特意打开游标 ,游标是自动打开的。

declare 
 
       cursor c_job   --定义一个游标,并给它起个名字叫c_job 
       is 
       select name,sex,dept 
       from emp; 
 
       c_row c_job%rowtype;   --定义一个游标变量c_row,该类型为游标c_job中的一行数据类型 
begin 
       for c_row in c_job loop 
         dbms_output.put_line(c_row.name||'-'||c_row.sex||'-'||c_row.dept); 
       end loop; 
end;

说明:可以看到该循环形式非常简单,隐含了记录变量的定义、游标的打开、提取和关闭过程。c_row为隐含定义的记录变量,循环的执行次数与游标取得的数据的行数相一致。

–Fetch游标
–使用的时候必须要明确的打开和关闭

declare  
       cursor c_job --定义一个游标,并给它起个名字叫c_job 
       is 
       select name,sex,dept 
       from emp; 
       c_row c_job%rowtype;    --定义一个游标变量 
begin 
       open c_job;     --打开游标 
         loop 
 
           fetch c_job into c_row;   --提取一行数据到c_row 
           --判读是否提取到值,没取到值就退出 
           --取到值c_job%notfound 是false  
           --取不到值c_job%notfound 是true 
           exit when c_job%notfound; 
            dbms_output.put_line(c_row.name||'-'||c_row.sex||'-'||c_row.dept); 
         end loop; 
       --关闭游标 
      close c_job; 
end;

下面这个栗子是一个update操作,我们可以用隐式游标sql的属性%found,%notfound,%rowcount,%isopen观察update语句的执行情况。

      begin 
         update emp set sex='女' WHERE name='jasom'; 
         if sql%isopen then 
           dbms_output.put_line('Openging'); 
           else 
             dbms_output.put_line('closing'); 
             end if; 
          if sql%found then 
            dbms_output.put_line('找到了相关数据,游标指向了有效行');--判断游标是否指向有效行 
            else 
              dbms_output.put_line('没有找到符合条件的数据行'); 
              end if; 
              if sql%notfound then 
                dbms_output.put_line('执行完毕'); 
                else 
                  dbms_output.put_line('正在执行'); 
                  end if; 
                   dbms_output.put_line('本次操作共更改'||sql%rowcount||'行'); 
                   exception  
                     when no_data_found then 
                       dbms_output.put_line('找不到可操作的数据'); 
                       when too_many_rows then 
                         dbms_output.put_line('Too Many rows'); 
                         end; 
declare 
       empname emp.name%TYPE; 
       empdept emp.dept%TYPE; 
       begin 
         if sql%isopen then 
           dbms_output.put_line('Cursor is opinging'); 
           else 
             dbms_output.put_line('Cursor is Close'); 
             end if; 
             if sql%notfound then 
               dbms_output.put_line('No Value'); 
               else 
                 dbms_output.put_line(empname); 
                 end if; 
                 dbms_output.put_line('共检索到'||sql%rowcount||'行数据'); 
                 dbms_output.put_line('-------------'); 
 
                 select name,dept into  empname,empdept from emp where name='tom'; 
                 dbms_output.put_line('共检索到'||sql%rowcount||'行数据'); 
 
                if sql%isopen then 
                dbms_output.put_line('Cursor is opinging'); 
                else 
                dbms_output.put_line('Cursor is Closing'); 
                end if; 
                 if sql%notfound then 
                 dbms_output.put_line('No Value'); 
                 else 
                 dbms_output.put_line('检索出的员工是:'||empname); 
                 end if; 
                 exception  
                   when no_data_found then 
                     dbms_output.put_line('No Value'); 
                     when too_many_rows then 
                       dbms_output.put_line('too many rows'); 
                       end; 

–使用游标和loop循环来显示所有部门的名称
–游标声明
–遇到for循环不必亲自打开关闭游标哦!

declare  
       cursor csr_dept 
       is 
       --select语句 
       select distinct dept 
       from emp; 
       --指定行指针,这句话应该是指定和csr_dept行类型相同的变量 
       row_dept csr_dept%rowtype; 
begin 
       --for循环 
       for row_dept in csr_dept loop 
           dbms_output.put_line('部门名称:'||row_dept.dept); 
       end loop; 
end;

–使用游标和while循环来显示所有部门的职员(用%found属性)

declare 
       --游标声明 
       cursor csr_TestWhile 
       is 
       --select语句 
       select  name,dept 
       from emp; 
       --指定行指针 
       row_loc csr_TestWhile%rowtype; 
begin 
  --打开游标 
       open csr_TestWhile; 
       --给第一行喂数据 
       fetch csr_TestWhile into row_loc; 
       --测试是否有数据,并执行循环 
         while csr_TestWhile%found loop 
           dbms_output.put_line('职员'||row_loc.name||'在'||row_loc.dept||'部门'); 
           --给下一行喂数据 
           fetch csr_TestWhile into row_loc; 
         end loop; 
       close csr_TestWhile; 
end; 

–向游标传递一个部门名称,用for循环和游标,打印出此部门的所有雇员的所有信息(使用循环游标)
–CURSOR cursor_name[(parameter[,parameter],…)] IS select_statement;
–定义参数的语法如下:Parameter_name [IN] data_type[{:=|DEFAULT} value]

declare  
      CURSOR  
      c_dept(p_dept varchar2) 
      is 
      select * from emp where emp.dept=p_dept; 
      r_emp emp%rowtype; 
begin 
        for r_emp in c_dept('IT') loop 
            dbms_output.put_line('员工名:'||r_emp.name||'性别:'||r_emp.sex); 
        end loop; 
end;

–向游标传递一个姓名,显示此职员的所有信息(使用参数游标)

declare  
       cursor 
       c_name(p_name nvarchar2) 
       is  
       select * from emp where name=p_name; 
       r_name emp%rowtype; 
begin  
       for r_name in c_name('tom') loop 
           dbms_output.put_line('员工名'||r_name.name||' '||'员工性别'||r_name.sex||'所属部门'||r_name.dept); 
        end loop; 
end;

–编写一个PL/SQL程序块,对所有的职员名字前增加前缀“superman-”

declare 
      cursor 
          csr_Addname 
      is 
          select * from emp  FOR UPDATE OF name; 
     r_Addname  emp%rowtype; 
 
begin 
    for r_Addname in csr_Addname LOOP 
 
         UPDATE EMP SET name= 'superman-'||r_Addname.name; 
    END LOOP; 
END;

升级一下我的emp表,以完成接下来的操作~

alter table  emp add(hiredate  date) --添加一个叫做hiredate的字段,数据自己update一下就可以

–编写一个PL/SQL程序块,以提升2个入职时间最久的职员为MANAGER
–(提示:可以定义一个变量作为计数器控制游标只提取两条数据;也可以在声明游标的时候把雇员中资格最老的两个人查出来放到游标中。)

declare 
    cursor crs_testComput 
    is 
    select * from emp order by HIREDATE asc; 
    --计数器 
    top_two number:=2; 
    r_testComput crs_testComput%rowtype; 
begin 
    open crs_testComput; 
       FETCH crs_testComput INTO r_testComput; 
          while top_two>0 loop 
             dbms_output.put_line('员工姓名:'||r_testComput.NAME||' 入职时间:'||r_testComput.HIREDATE); 
             --计数器减一 
             top_two:=top_two-1; 
             FETCH crs_testComput INTO r_testComput; 
           end loop; 
     close crs_testComput; 
end;

–编写一个PL/SQL程序块,对所有雇员按他们的基本薪水(sal)的20%为他们加薪,
–如果增加的薪水大于300就取消加薪(对emp1表进行修改操作,并将更新前后的数据输出出来)

DECLARE  
  CURSOR  CUR_TEST 
  is 
  SELECT  * FROM   EMP  FOR UPDATE OF SAL; 
  CUR CUR_TEST%ROWTYPE; 
  sal_add  emp.sal%type; 
  sal_info emp.sal%type; 
  BEGIN 
 
 
      FOR  CUR  IN  CUR_TEST  LOOP 
 
      sal_add:=CUR.SAL*0.2; 
 
      IF   sal_add<300  then 
        sal_info:=cur.sal+sal_add; 
         DBMS_OUTPUT.put_line('原工资为'||CUR.SAL||'现在涨薪到'||sal_info); 
 
         else   
           sal_info:=CUR.SAL; 
           dbms_output.put_line('员工资为'||SAL_INFO||'保持原有工资'); 
           end  if; 
           update emp  set sal=sal_info where current of  cur_test; 
           END  LOOP; 
           end; 

–将每位员工工作了多少年零多少月零多少天输出出来
–近似
–CEIL(n)函数:取大于等于数值n的最小整数
–FLOOR(n)函数:取小于等于数值n的最大整数
–truc的用法 http://jingyan.baidu.com/article/c275f6ba2d2347e33c756753.html

declare 
  cursor 
   crs_WorkDay 
   is 
   select ENAME,HIREDATE, trunc(months_between(sysdate, hiredate) / 12) AS SPANDYEARS, 
       trunc(mod(months_between(sysdate, hiredate), 12)) AS months, 
       trunc(mod(mod(sysdate - hiredate, 365), 12)) as days 
   from emp1; 
  r_WorkDay crs_WorkDay%rowtype; 
begin 
    for   r_WorkDay in crs_WorkDay loop 
    dbms_output.put_line(r_WorkDay.ENAME||'已经工作了'||r_WorkDay.SPANDYEARS||'年,零'||r_WorkDay.months||'月,零'||r_WorkDay.days||'天'); 
    end loop; 
end;

以上便是关于游标的使用教程,大家如果有建议和分享可以在评论栏指出,很高兴可以分享交流相关知识!

原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/13838.html

(0)
上一篇 2021年7月19日
下一篇 2021年7月19日

相关推荐

发表回复

登录后才能评论