oracle数据库游标的再次深入学习

游标的概念:

在PL/SQL块中执行查询语句(select)和数据操纵(DML)时,Oracle会在内存中分配一个缓冲区,缓冲区包含了处理过程的必要信息,包括已处理的行数,指向被分析的指针和查询情况下的活动集,即查询语句返回的数据行集,该缓冲区域称为上下文区,游标就是指向该缓冲区域的句柄指针。

为了处理select语句返回多行数据的情况,可以使用游标处理多行信息,也可以使用select BULK COLLECT INTO语句处理多行数据。

游标分为两类:

  1. 显式游标:用户定义、操作,用户处理返回多行数据的select查询
  2. 隐式游标:系统自动进行操作,用于处理DML语句和返回单行的select 查询

DML: Data Manipulation Language 数据操纵语言

显式游标操作过程

利用显式游标处理select查询返回多行数据,需要预先定义显式游标,然后打开游标,检索游标,关闭游标等操作

定义游标

根据要查询的数据情况创建游标,在PL/SQL声明部分定义游标,语法为:

CURSOR cursor_name is select_statment

注意事项:

  1. 游标必须在声明部分进行定义;
  2. 游标在定义时可以应用PL/SQL变量,但是变量必须在声明游标之前声明
  3. 定义游标时并没有产生数据,只是将定义信息保存到数据字典中。
  4. 游标定义后可以用哪个Cursor%ROWTYPE定义记录变量;

打开游标

open cursor_name;

检索游标

fetch cursor_name into variable_list|record_variable;

关闭游标

close cursor_name;

举个栗子

根据输入产品编码查询该产品的库存信息

DECLARE 
v_sku  VARCHAR2(100);
cursor skuInfo is select  s.sku,sum(d.qty) qty from sku_table s join inv_table d on d.sku = s.sku where s.sku = v_sku  group by s.sku;
v_skuInfo skuInfo%rowtype;
begin
v_sku:=&sku;
DBMS_OUTPUT.put_line(v_sku);
open skuInfo;
loop
   fetch skuInfo into v_skuInfo ;
   exit when skuInfo%notfound;
   DBMS_OUTPUT.PUT_LINE('sku:'|| v_skuInfo.sku || ' 库存量:'|| v_skuInfo.qty);
end loop;
close skuInfo;
end;

显示游标属性

利用游标属性可以判断当前游标的状态,显式游标的属性如下

属性名称 返回数据类型 注释
%ISOPEN 布尔型 用于判断游标是否打开,如果已经打开返回true,反之返回false
%FOUND 布尔型 判断最近一次使用fetch语句时是否存缓冲区检索到数据,如果监测到返回true,反之false
%NOTFOUND 布尔型 与%found相反
%ROWTCOUNT 数值型 返回目前为止从游标缓冲区检索到的记录个数
%BULK_ROWCOUNT 数值型 用于取的forall语句执行批绑定操作时第i个元素所影响的行数

显式游标检索

  1. 利用简单循环

    利用简单循环游标的基本方式为:

    declare
    cursor cursor_name is select .....
    begin
     open cursor_name;
     loop 
            fetch ... into ....;
            exit when cursor_name%notfound;
     end loop;
     close cursor_name;
    end;
    

    举个例子:

    查询成品表中的码垛方式

    declare 
    cursor v_header is select * from putaway_table;
    v_row v_header%rowtype;
    begin
        open v_header;
        loop
            fetch v_header into v_row;
            exit when v_header%notfound;
            DBMS_OUTPUT.PUT_LINE(nvl(v_row.PUTAWAYID,'未知') || ' adder is '|| v_row.editwho);
        end loop;
        close v_header;
    end;
    

exit when 子句应该是fetch...into...语句的下一条语句

  1. 利用while循环检索

    利用while循环检索游标的基本方式为:

    declare
     cursor cursor_name is select ....
    begin
     open cursor_name;
     fetch...into ...;
     while cursor_name%found loop
         fetch ... into ...;
     end loop;
     close cursor_name;
    end;
    

    举个例子:

    declare 
    cursor v_header is select * from putaway_table;
    v_row v_header%rowtype;
    begin
        open v_header;
        fetch  v_header into v_row;
        while v_header%found  loop
            fetch v_header into v_row;
            DBMS_OUTPUT.PUT_LINE(nvl(v_row.PUTAWAYID,'未知') || ' adder is '|| v_row.editwho);
            fetch  v_header into v_row;
        end loop;
        close v_header;
    end;
    

    在循环体外进行一次fetch操作,做我第一次循环的条件,然后在循环体内,完成任务计算之后,再进行一次fetch 作为下次循环的条件

  1. 利用for循环检索游标

    利用for检索游标时,系统会自动打开、检索、和关闭游标,用户只需要考虑如何处理游标缓冲区中检索的数据,

    declare
        cursor cursor_name is select...
        begin
            for loop_var in cursor_name loop
            .....
        end loop
    end
    

利用for循环遍历游标时,系统首先隐含定义一个数据类型为cursor_name%rowType的循环变量,loop_var,然后自动打开游标,从游标缓冲区内提取数据并放入loop_var变量中,同时进行%found属性监测,当所有数据检索遍历完毕之后,循环中断,系统自动关闭游标。

举个例子:利用for循环实现

declare 
cursor v_header is select * from putaway_table;

begin
   for v_row in v_header loop
           DBMS_OUTPUT.PUT_LINE(nvl(v_row.PUTAWAYID,'未知') || ' adder is '|| v_row.editwho);
   end loop;
end;

由于用for循环检索游标时,游标的打开、数据的检索,是否检索到数据的判断、游标的关闭都是由系统自动完成的,因此可以不在声明部分定义游标,而在for语句中直接使用子句,因此以上程序代码可以修改为:

begin
   for v_row in ( select * from putaway_table) loop
           DBMS_OUTPUT.PUT_LINE(nvl(v_row.PUTAWAYID,'未知') || ' adder is '|| v_row.editwho);
   end loop;
end;

总结:在实际程序编写过程中,个人建议用for循环遍历,为了程序的可读性提高,使用for循环的第二种模式是比较合适的

隐式游标

隐式游标用于处理返回多行数据的select查询,但所有的sql语句都有一个执行的缓冲区,隐式游标就是指向该缓冲区的指针,有系统隐含打开、处理、关闭。隐式游标又称为SQL游标。SQL游标依然由一下4个属性

属性名称 返回数据类型 注释
SQL%ISOPEN 布尔型 用于判断游标是否打开,如果已经打开返回true,反之返回false
SQL%FOUND 布尔型 判断最近一次使用fetch语句时是否存缓冲区检索到数据,如果监测到返回true,反之false
SQL%NOTFOUND 布尔型 与%found相反
SQL%ROWTCOUNT 数值型 返回目前为止从游标缓冲区检索到的记录个数

简单举个例子:将skugroup为henan的修改为shanghan,如果不存在则直接插入一条信息

begin 
    update sku_table set skugroup = 'shhanghai' where skugroup='henan';
    if SQL%NOTFOUND THEN
        INSERT INTO sku_table (SKU,SKU_NAME,SKUGROUP) 
        VALUES ('100002','大馅饼','shanghai');
    end if;
end;

游标变量

在介绍显式游标在定义时与特定的查询绑定,其结构是不变的,因此又称为静态游标,游标变量数第一个指向多行查询结果集的指针,不与特定的查询绑定,因此具有非常大的灵活性,可以在打开游标变量时进行查询,可以返回不同结果的结果集;

在PL/SQL中使用游标变量包括定义游标应用类型(ref cursor)、声明游标变量、打开游标变量、检索游标变量、关闭游标变量等几个步骤。

  1. 定义游标引用类型及游标变量,其语法如下

    type re_cursor_type_name is ref cursor [return return_tyoe]
    

    在oracle 11g中系统预定义了一个游标引用类型,称为ys_refcursor,可以直接使用它定义游标变量

  2. 声明游标变量,基本形式如下

    ref_cursor_type_name var_name;

    例如:

    type emp_cursor_type is ref cursor return table_name%rowtype;
    
    v_row emp_cursor_type ;
    
    my_cursor sys_refcursor;
    
  1. 打开游标变量

    open cursor_name form select_statement;

    如:v_row for select * from table_name;

  2. 检索游标

    检索游标的方法与静态游标类似,这里就不在赘述

  3. 关闭游标

    close cursor_var_name

举个简单的例子:

declare
type type_cursor is ref cursor;
v_cursor type_cursor;
begin
    open v_cursor for select * from table_name
end;
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,313评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,369评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,916评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,333评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,425评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,481评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,491评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,268评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,719评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,004评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,179评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,832评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,510评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,153评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,402评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,045评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,071评论 2 352

推荐阅读更多精彩内容