第8章 游标

target

了解什么是游标
掌握显示游标的使用
掌握隐士游标的使用

1. 什么是游标

游标的使用可以让用户像操作数组一样操作査询出来的数据集,这使得使用PL/SQL编程 更加方便。

1.1 游标的概念

可以将游标(Cursor)形象地看成一个变动的光标。它实际上是一个指针,它在一段 Oracle存放数据査询结果集或数据操作结果集的内存中,这个指针可以指向结果集中的任何一 条记录,这样就可以得到它所指向的数据了。

初始时它指向首记录。这种模型很像编程语言中的数组。

可以简单地理解游标为指向结果集记录的指针,利用游标可以返回它当前指向的行记录 (只能返回一行记录)。如果要返回多行,那么需要不断地滚动游标,把想要的数据査询一遍。

1.2游标的种类

Oracle中游标分为静态游标和REF游标两类。其中,静态游标就像一个数据快照,打开游标后的结果集是对数据库数据的一个备份,数据不随着对表执行DML操作后而改变。从这个特性来说,结果集是静态的。

静态游标包含如下两种类型:

  • 显式游标
  • 隐式游标

2. 显示游标

2.1 语法

CURSOR cursor_name
    [(parameter_name datatype,...))
IS select_statement;

【语法说明】

  • CURSOR cursor_name:声明游标,cursor_name是游标的名称。
  • parameter_name:参数名称。
  • datatype:参数类型。
  • select_statement:游标关联的SELECT语句,但该语句不能是SELECT...INTO...语句。

2.2 显式游标的属性

利用游标属性可以得到游标执行的相关信息。显式游标有以下4个属性:

  • %ISOPEN:用于判断游标是否打开,如果已经打开则返回TRUE,如果游标未打开则 返回FALSE。
  • %FOUND:此属性可用来检测行数据是否有效。如果有效属性返回TRUE,否则返回FALSE。
  • %NOTFOUND:与%FOUND属性恰好相反,如果没有提取出数据则返回TRUE,否则返回FALSE。
  • %ROWCOUNT:累计到当前为止使用FETCH提取数据的行数。

🌰:%ISOPEN的使用

declare 
    cursor product_isopen_cur is 
        select * from productinfo;
        
    cur_product_cd productinfo%rowtype;
        
begin
    if product_isopen_cur%isopen then
        fetch product_isopen_cur into cur_product_cd;
        dbms_output.put_line('产品名称' || cur_product_cd.productname || '产品价格:'|| cur_product_cd.productprice );
    else
        dbms_output.put_line('游标没有打开!');
    end if;
end;
/   

输出:

游标没有打开!

PL/SQL 过程已成功完成。

2.3 游标的使用步骤

(1) 声明游标

声明游标主要用来给游标命名并且使得游标关联一个査询。具体语句如下:

declare 
  cursor cursor_Name is select_statement;

(2) 打开游标

游标中任何对数据的操作都是建立在游标被打开的前提下。打开游标初始化了游标指针, 游标一旦打开,其结果集都是静态的。也就是说,结果集此时不会反映出数据库中对数据进行 的增加、删除、修改操作。具体语句如下:

OPEN cursor_Name

(3) 读取数据

读取数据要利用FETCH语句完成,它可以把游标指向位置的记录放入到PL/SQL声明的变量当中。

它只能取出指针当前行的记录。

正常情况下,FETCH要和循环语句一起使用,这样指针会不断前进,直到某个条件不符合要求而退出。

使用FETCH时游标属性%ROWCOUNT会不断累加。具体语句如下:

FETCH cursor_Name INTO Record_Name;

(4) 关闭游标

关闭某个名称的游标。此时释放资源,结果集中的数据将不能做任何操作。

🌰:创建一个简单的游标

declare 
    cursor pdct_cur
    is select * from productinfo;
    
    product_cd productinfo%rowtype;
    
begin
    open pdct_cur;
        fetch pdct_cur into product_cd;
        dbms_output.put_line(product_cd.productid || '-' || product_cd.productname || '-' || product_cd.productprice);
    close pdct_cur;
end;
/

输出:

1-华为Mate40Pro-7999

PL/SQL 过程已成功完成。

只输出了一条记录,也就是前面说到的游标指针默认指向第一条记录,若想获取其他记录,要用循环去移动指针,然后读取记录。

2.4 游标中的loop语句

通常游标提取的数据不会是一条,而是多条记录。这样就需要一个遍历结果集的方式--loop语句。

🌰:使用loop循环获取多条记录

declare 
    cursor product_loop_cur
    is select productid,productname,productprice from productinfo
         where productprice > 3000;
         
    cur_productid productinfo.productid%type;
    cur_productname productinfo.productname%type;
    cur_productprice productinfo.productprice%type;
    
begin
    open product_loop_cur;
        loop
            fetch product_loop_cur into cur_productid,cur_productname,cur_productprice;
            exit when product_loop_cur%notfound;
            dbms_output.put_line('产品id:' || cur_productid || '  产品名称:' || cur_productname || '  产品价格:' || cur_productprice);
        end loop;
    close product_loop_cur;
end;
/

输出:

产品id:1  产品名称:华为Mate40Pro  产品价格:7999
产品id:2  产品名称:华为P40Pro  产品价格:6999
产品id:4  产品名称:华为MatePadPro  产品价格:4999
产品id:5  产品名称:华为智慧屏  产品价格:8999
产品id:6  产品名称:华为智慧屏2代  产品价格:9999
产品id:7  产品名称:华为智慧屏2代  产品价格:9999

PL/SQL 过程已成功完成。

2.5 使用bulk collect和for语句的游标

游标中通常使用FETCH...INTO...语句提取数据,这种方式是单条数据提取,在数据量很大的情况下执行效率不是很理想。而FETCH...BULK COLLECT INTO语句可以批量提取数据,在数据量大的情况下它的执行效率比单条提取数据的高。

🌰:

declare
    cursor product_collect_cur
    is select * from productinfo;
    
    type prot_table is table of productinfo%rowtype;
    pdct_rd prot_table;
    
begin
    open product_collect_cur;
    loop
        --从游标中提取数据,每次取2条
      fetch product_collect_cur bulk collect into pdct_rd limit 2;
        for i in 1..pdct_rd.count loop
          dbms_output.put_line('产品id:' || pdct_rd(i).productid || '  产品名称:' || pdct_rd(i).productname || ' 产品价格:' || pdct_rd(i).productprice);
        end loop;
        exit when product_collect_cur%notfound;
    end loop;
    close product_collect_cur;
end;
/

2.6 使用 CURSOR FOR LOOP

游标很多机会都是迭代结果集,在PL/SQL这个过程中可以使用更简单的方式实现, CURSOR FOR LOOP不需要特别的声明变量。

declare 
    cursor cfl is 
        select productname,productprice from productinfo
        where productprice > 3000;
begin
    --把游标里面的数据放到curcfl中,该类型是%rowtype
    for curcfl in cfl loop
        dbms_output.put_line('名称:' || curcfl.productname || ' 产品价格:' || curcfl.productprice);
    end loop;
end;
/

输出:

名称:华为Mate40Pro 产品价格:7999
名称:华为P40Pro 产品价格:6999
名称:华为MatePadPro 产品价格:4999
名称:华为智慧屏 产品价格:8999
名称:华为智慧屏2代 产品价格:9999
名称:华为智慧屏2代 产品价格:9999

PL/SQL 过程已成功完成。

2.7 带参数的游标

在使用显式游标时是可以指定参数的,指定的参数包括参数的顺序和参数的类型。参数可 以传递给游标在査询中使用,这样就方便了用户根据不同的査询条件进行査询,也方便了游标 在存储过程中的使用。

declare
    cur_productname productinfo.Productname%TYPE := '华为'; 
    cur_productprice productinfo.Productprice%TYPE := 1200; 
    cur_prodrcd productinfo%ROWTYPE;

    CURSOR pdct_parameter_cur (name VARCHAR,price NUMBER) IS 
    SELECT * FROM PRODUCTINFO
    WHERE productname like name||'%' AND productprice > price;
begin
  OPEN pdct_parameter_cur(cur_productname,cur_productprice);
    Loop
      FETCH pdct_parameter_cur INTO cur_prodrcd;
      EXIT WHEN pdct_parameter_cur%NOTFOUND;
      DBMS_OUTPUT.PUT_LINE('产品ID, ' || cur_prodrcd.productid || ' 产品名称: ' || cur_prodrcd.productname || ' 产品价格:'|| cur_prodrcd.productprice);
    END LOOP;
    CLOSE pdct_parameter_cur;
END;
/

输出:

产品ID, 1 产品名称: 华为Mate40Pro 产品价格:7999
产品ID, 2 产品名称: 华为P40Pro 产品价格:6999
产品ID, 3 产品名称: 华为荣耀10 产品价格:1999
产品ID, 4 产品名称: 华为MatePadPro 产品价格:4999
产品ID, 5 产品名称: 华为智慧屏 产品价格:8999
产品ID, 6 产品名称: 华为智慧屏2代 产品价格:9999
产品ID, 7 产品名称: 华为智慧屏2代 产品价格:9999

PL/SQL 过程已成功完成。

3. 隐式游标

隐式游标和显式游标有所差异,它虽然没有显式游标一样的可操作性,但在实际的工作当 中也经常用到。

3.1隐式游标的特点

每当运行SELECT或DML语句时,PL/SQL会打开一个隐式的游标。隐式游标不受用户的控制,这一点和显式游标有明显的不同。下面列出了隐式游标和显式游标的不同处:

  • 隐式游标由PL/SQL自动管理,
  • 隐式游标中的%ISOPEN属性永远返回FALSE
  • 隐式游标的默认名称是SQL
  • SELECT或DML操作产生隐式游标:
  • 隐式游标的属性值始终是最新执行的SQL语句的。
DECLARE 
    cur_productname productinfo.Productname%TYPE;
    cur_productprice productinfo.Productprice%TYPE;
BEGIN
    SELECT productname, productprice INTO cur_productname,cur_productprice
    FROM PRODUCTINFO
    where productid =   '1';
    IF SQL%FOUND THEN
        DBMS_OUTPUT.PUT_LINE('产品名称:' || cur_productname || ', 产品价格:' || cur_productprice);
    END IF ;
END;
/

输出:

产品名称:华为Mate40Pro, 产品价格:7999

PL/SQL 过程已成功完成。

3.2 游标中使用异常处理

使用游标时,某些情况下得到的数据超出了控制范围,如果不加处理会出现脚本执行中断的情况。这种情况下,脚本开发者通常会使用异常处理来维护脚本的稳定性。

🌰:在游标中使用异常处理

DECLARE
    cur_productname productinfo.Productname%TYPE;
    cur_productprice productinfo.Productprice%TYPE;
BEGIN   
    SELECT productname, productprice INTO cur_productname,cur_productprice
    FROM PRODUCTINFO
    where productid ='1';

    IF SQL%FOUND THEN
        DBMS_OUTPUT.PUT_LINE('产品名称:' || cur_productname || ' ,产品价格:' || cur_productprice);
    END IF;
EXCEPTION
  WHEN NO_DATA_FOUND THEN
    DBMS_OUTPUT.PUT_LINE('没有数据!');
  WHEN TOO_MANY_ROWS THEN
    DBMS_OUTPUT.PUT_LINE('数据过多!');
END;
/

输出:

产品名称:华为Mate40Pro ,产品价格:7999

PL/SQL 过程已成功完成。

4. 游标案例

本案例涉及到两张表,分别是EMP(员工表)和DEPT(部门表)。

要求:

利用游标转换这两张表的数据,要求把月薪高于1500的,部门地址为“CHICAGO”和“NEW YORK”的员工放到EMP_TEMP表中,月薪低于2500的上调5%。

分析:

第一步:建表 EMP_TEMP

第二步:创建游标存储符合条件的数据。

第三步:把符合要求的数据放进新表

第四步:把工资上调

操作步骤:

建表

create table EMP_TEMP as 
    select * from emp where 1=0;

编写游标案例:

declare 
    cur_empno emp.empno%type;
    cur_ename emp.ename%type;
    cur_emp_cd emp%rowtype;
    
    --创建游标存储符合条件的数据
    cursor cur_emp is
        select * from emp where sal>1500 and DEPTNO 
        in (select DEPTNO from dept where loc in ('CHICAGO','NEW YORK'));
begin   

    --把符合条件的数据存到表中
    open cur_emp;
    loop
      fetch cur_emp into cur_emp_cd;
      if cur_emp%FOUND then
        insert into emp_temp(empno,ename,job,mgr,hiredate,sal,comm,deptno)
        values(cur_emp_cd.empno,cur_emp_cd.ename,cur_emp_cd.job,cur_emp_cd.mgr,cur_emp_cd.hiredate,cur_emp_cd.sal,cur_emp_cd.comm,cur_emp_cd.deptno);
      else
        dbms_output.put_line('已经取出所有数据!共' || cur_emp%rowcount || '条记录');
        exit;
      end if;
    end loop;
    commit;
    close cur_emp;
    
    --调整工资
    update emp_temp 
    set sal = sal * 1.05
    where sal < 2500;
    
    dbms_output.put_line('工资调整完毕!共调整' || SQL%ROWCOUNT || '条记录');
    commit;
end;
/

输出:

已经取出所有数据!共4条记录
工资调整完毕!共调整2条记录

PL/SQL 过程已成功完成。

习题

一、填空题

1.静态游标包含 ( )和( ) 两种类型。
2.通常使用( )命令遍历游标的数据集。
3.游标的使用步骤包括 ( )、( )、( )、( )
4.游标4个属性是 ( )、( )、( )、( )

二、选择题

  1. 如果游标没有被打开时就调用,会不会提示出错?( )
    A.不会 B.会
  2. 下面有关隐式游标中的%ISOPEN属性,说法正确的是( ).
    A. 隐式游标中的%ISOPEN属性永远返回FALSE
    B. 隐式游标中的%ISOPEN属性可以被人为地控制
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,445评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,889评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,047评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,760评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,745评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,638评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,011评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,669评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,923评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,655评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,740评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,406评论 4 320
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,995评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,961评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,023评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,483评论 2 342

推荐阅读更多精彩内容

  • 在 PL/SQL 程序中,对于处理多行记录的事务经常使用游标来实现。 游标概念 为了处理 SQL 语句,ORACL...
    辽A丶孙悟空阅读 521评论 0 15
  • 前言 厚积而薄发。 在 PL/SQL 程序中,对于处理多行记录的事务经常使用游标来实现。 游标的概念 --为了处理...
    olaH阅读 981评论 0 4
  • SQL SERVER 参考: 游标(Cursor)的讲解与实例 在数据库中,游标是一个十分重要的概念。游标提供了一...
    1edee8a22fc9阅读 317评论 0 0
  • 一、简介 1、游标的概念 游标(Cursor) 就是一个变动的光标,它本质上是一个指针,指向从数据库查询出来的结果...
    滴滴滴9527阅读 2,027评论 0 1
  • 2019-05-13 游标(cursor)能够根据查询条件从数据表中提取一组记录,将其作为一个临时表置于数据缓冲区...
    Mr_J316阅读 4,067评论 0 0