第8章 游標(biāo)

target

了解什么是游標(biāo)
掌握顯示游標(biāo)的使用
掌握隱士游標(biāo)的使用

1. 什么是游標(biāo)

游標(biāo)的使用可以讓用戶像操作數(shù)組一樣操作査詢出來(lái)的數(shù)據(jù)集,這使得使用PL/SQL編程 更加方便。

1.1 游標(biāo)的概念

可以將游標(biāo)(Cursor)形象地看成一個(gè)變動(dòng)的光標(biāo)。它實(shí)際上是一個(gè)指針,它在一段 Oracle存放數(shù)據(jù)査詢結(jié)果集或數(shù)據(jù)操作結(jié)果集的內(nèi)存中,這個(gè)指針可以指向結(jié)果集中的任何一 條記錄,這樣就可以得到它所指向的數(shù)據(jù)了。

初始時(shí)它指向首記錄。這種模型很像編程語(yǔ)言中的數(shù)組。

可以簡(jiǎn)單地理解游標(biāo)為指向結(jié)果集記錄的指針,利用游標(biāo)可以返回它當(dāng)前指向的行記錄 (只能返回一行記錄)。如果要返回多行,那么需要不斷地滾動(dòng)游標(biāo),把想要的數(shù)據(jù)査詢一遍。

1.2游標(biāo)的種類

Oracle中游標(biāo)分為靜態(tài)游標(biāo)和REF游標(biāo)兩類。其中,靜態(tài)游標(biāo)就像一個(gè)數(shù)據(jù)快照,打開(kāi)游標(biāo)后的結(jié)果集是對(duì)數(shù)據(jù)庫(kù)數(shù)據(jù)的一個(gè)備份,數(shù)據(jù)不隨著對(duì)表執(zhí)行DML操作后而改變。從這個(gè)特性來(lái)說(shuō),結(jié)果集是靜態(tài)的。

靜態(tài)游標(biāo)包含如下兩種類型:

  • 顯式游標(biāo)
  • 隱式游標(biāo)

2. 顯示游標(biāo)

2.1 語(yǔ)法

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

【語(yǔ)法說(shuō)明】

  • CURSOR cursor_name:聲明游標(biāo),cursor_name是游標(biāo)的名稱。
  • parameter_name:參數(shù)名稱。
  • datatype:參數(shù)類型。
  • select_statement:游標(biāo)關(guān)聯(lián)的SELECT語(yǔ)句,但該語(yǔ)句不能是SELECT...INTO...語(yǔ)句。

2.2 顯式游標(biāo)的屬性

利用游標(biāo)屬性可以得到游標(biāo)執(zhí)行的相關(guān)信息。顯式游標(biāo)有以下4個(gè)屬性:

  • %ISOPEN:用于判斷游標(biāo)是否打開(kāi),如果已經(jīng)打開(kāi)則返回TRUE,如果游標(biāo)未打開(kāi)則 返回FALSE。
  • %FOUND:此屬性可用來(lái)檢測(cè)行數(shù)據(jù)是否有效。如果有效屬性返回TRUE,否則返回FALSE。
  • %NOTFOUND:與%FOUND屬性恰好相反,如果沒(méi)有提取出數(shù)據(jù)則返回TRUE,否則返回FALSE。
  • %ROWCOUNT:累計(jì)到當(dāng)前為止使用FETCH提取數(shù)據(jù)的行數(shù)。

??:%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('產(chǎn)品名稱' || cur_product_cd.productname || '產(chǎn)品價(jià)格:'|| cur_product_cd.productprice );
    else
        dbms_output.put_line('游標(biāo)沒(méi)有打開(kāi)!');
    end if;
end;
/   

輸出:

游標(biāo)沒(méi)有打開(kāi)!

PL/SQL 過(guò)程已成功完成。

2.3 游標(biāo)的使用步驟

(1) 聲明游標(biāo)

聲明游標(biāo)主要用來(lái)給游標(biāo)命名并且使得游標(biāo)關(guān)聯(lián)一個(gè)査詢。具體語(yǔ)句如下:

declare 
  cursor cursor_Name is select_statement;

(2) 打開(kāi)游標(biāo)

游標(biāo)中任何對(duì)數(shù)據(jù)的操作都是建立在游標(biāo)被打開(kāi)的前提下。打開(kāi)游標(biāo)初始化了游標(biāo)指針, 游標(biāo)一旦打開(kāi),其結(jié)果集都是靜態(tài)的。也就是說(shuō),結(jié)果集此時(shí)不會(huì)反映出數(shù)據(jù)庫(kù)中對(duì)數(shù)據(jù)進(jìn)行 的增加、刪除、修改操作。具體語(yǔ)句如下:

OPEN cursor_Name

(3) 讀取數(shù)據(jù)

讀取數(shù)據(jù)要利用FETCH語(yǔ)句完成,它可以把游標(biāo)指向位置的記錄放入到PL/SQL聲明的變量當(dāng)中。

它只能取出指針當(dāng)前行的記錄。

正常情況下,F(xiàn)ETCH要和循環(huán)語(yǔ)句一起使用,這樣指針會(huì)不斷前進(jìn),直到某個(gè)條件不符合要求而退出。

使用FETCH時(shí)游標(biāo)屬性%ROWCOUNT會(huì)不斷累加。具體語(yǔ)句如下:

FETCH cursor_Name INTO Record_Name;

(4) 關(guān)閉游標(biāo)

關(guān)閉某個(gè)名稱的游標(biāo)。此時(shí)釋放資源,結(jié)果集中的數(shù)據(jù)將不能做任何操作。

??:創(chuàng)建一個(gè)簡(jiǎn)單的游標(biāo)

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 過(guò)程已成功完成。

只輸出了一條記錄,也就是前面說(shuō)到的游標(biāo)指針默認(rèn)指向第一條記錄,若想獲取其他記錄,要用循環(huán)去移動(dòng)指針,然后讀取記錄。

2.4 游標(biāo)中的loop語(yǔ)句

通常游標(biāo)提取的數(shù)據(jù)不會(huì)是一條,而是多條記錄。這樣就需要一個(gè)遍歷結(jié)果集的方式--loop語(yǔ)句。

??:使用loop循環(huán)獲取多條記錄

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('產(chǎn)品id:' || cur_productid || '  產(chǎn)品名稱:' || cur_productname || '  產(chǎn)品價(jià)格:' || cur_productprice);
        end loop;
    close product_loop_cur;
end;
/

輸出:

產(chǎn)品id:1  產(chǎn)品名稱:華為Mate40Pro  產(chǎn)品價(jià)格:7999
產(chǎn)品id:2  產(chǎn)品名稱:華為P40Pro  產(chǎn)品價(jià)格:6999
產(chǎn)品id:4  產(chǎn)品名稱:華為MatePadPro  產(chǎn)品價(jià)格:4999
產(chǎn)品id:5  產(chǎn)品名稱:華為智慧屏  產(chǎn)品價(jià)格:8999
產(chǎn)品id:6  產(chǎn)品名稱:華為智慧屏2代  產(chǎn)品價(jià)格:9999
產(chǎn)品id:7  產(chǎn)品名稱:華為智慧屏2代  產(chǎn)品價(jià)格:9999

PL/SQL 過(guò)程已成功完成。

2.5 使用bulk collect和for語(yǔ)句的游標(biāo)

游標(biāo)中通常使用FETCH...INTO...語(yǔ)句提取數(shù)據(jù),這種方式是單條數(shù)據(jù)提取,在數(shù)據(jù)量很大的情況下執(zhí)行效率不是很理想。而FETCH...BULK COLLECT INTO語(yǔ)句可以批量提取數(shù)據(jù),在數(shù)據(jù)量大的情況下它的執(zhí)行效率比單條提取數(shù)據(jù)的高。

??:

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
        --從游標(biāo)中提取數(shù)據(jù),每次取2條
      fetch product_collect_cur bulk collect into pdct_rd limit 2;
        for i in 1..pdct_rd.count loop
          dbms_output.put_line('產(chǎn)品id:' || pdct_rd(i).productid || '  產(chǎn)品名稱:' || pdct_rd(i).productname || ' 產(chǎn)品價(jià)格:' || pdct_rd(i).productprice);
        end loop;
        exit when product_collect_cur%notfound;
    end loop;
    close product_collect_cur;
end;
/

2.6 使用 CURSOR FOR LOOP

游標(biāo)很多機(jī)會(huì)都是迭代結(jié)果集,在PL/SQL這個(gè)過(guò)程中可以使用更簡(jiǎn)單的方式實(shí)現(xiàn), CURSOR FOR LOOP不需要特別的聲明變量。

declare 
    cursor cfl is 
        select productname,productprice from productinfo
        where productprice > 3000;
begin
    --把游標(biāo)里面的數(shù)據(jù)放到curcfl中,該類型是%rowtype
    for curcfl in cfl loop
        dbms_output.put_line('名稱:' || curcfl.productname || ' 產(chǎn)品價(jià)格:' || curcfl.productprice);
    end loop;
end;
/

輸出:

名稱:華為Mate40Pro 產(chǎn)品價(jià)格:7999
名稱:華為P40Pro 產(chǎn)品價(jià)格:6999
名稱:華為MatePadPro 產(chǎn)品價(jià)格:4999
名稱:華為智慧屏 產(chǎn)品價(jià)格:8999
名稱:華為智慧屏2代 產(chǎn)品價(jià)格:9999
名稱:華為智慧屏2代 產(chǎn)品價(jià)格:9999

PL/SQL 過(guò)程已成功完成。

2.7 帶參數(shù)的游標(biāo)

在使用顯式游標(biāo)時(shí)是可以指定參數(shù)的,指定的參數(shù)包括參數(shù)的順序和參數(shù)的類型。參數(shù)可 以傳遞給游標(biāo)在査詢中使用,這樣就方便了用戶根據(jù)不同的査詢條件進(jìn)行査詢,也方便了游標(biāo) 在存儲(chǔ)過(guò)程中的使用。

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('產(chǎn)品ID, ' || cur_prodrcd.productid || ' 產(chǎn)品名稱: ' || cur_prodrcd.productname || ' 產(chǎn)品價(jià)格:'|| cur_prodrcd.productprice);
    END LOOP;
    CLOSE pdct_parameter_cur;
END;
/

輸出:

產(chǎn)品ID, 1 產(chǎn)品名稱: 華為Mate40Pro 產(chǎn)品價(jià)格:7999
產(chǎn)品ID, 2 產(chǎn)品名稱: 華為P40Pro 產(chǎn)品價(jià)格:6999
產(chǎn)品ID, 3 產(chǎn)品名稱: 華為榮耀10 產(chǎn)品價(jià)格:1999
產(chǎn)品ID, 4 產(chǎn)品名稱: 華為MatePadPro 產(chǎn)品價(jià)格:4999
產(chǎn)品ID, 5 產(chǎn)品名稱: 華為智慧屏 產(chǎn)品價(jià)格:8999
產(chǎn)品ID, 6 產(chǎn)品名稱: 華為智慧屏2代 產(chǎn)品價(jià)格:9999
產(chǎn)品ID, 7 產(chǎn)品名稱: 華為智慧屏2代 產(chǎn)品價(jià)格:9999

PL/SQL 過(guò)程已成功完成。

3. 隱式游標(biāo)

隱式游標(biāo)和顯式游標(biāo)有所差異,它雖然沒(méi)有顯式游標(biāo)一樣的可操作性,但在實(shí)際的工作當(dāng) 中也經(jīng)常用到。

3.1隱式游標(biāo)的特點(diǎn)

每當(dāng)運(yùn)行SELECT或DML語(yǔ)句時(shí),PL/SQL會(huì)打開(kāi)一個(gè)隱式的游標(biāo)。隱式游標(biāo)不受用戶的控制,這一點(diǎn)和顯式游標(biāo)有明顯的不同。下面列出了隱式游標(biāo)和顯式游標(biāo)的不同處:

  • 隱式游標(biāo)由PL/SQL自動(dòng)管理,
  • 隱式游標(biāo)中的%ISOPEN屬性永遠(yuǎn)返回FALSE
  • 隱式游標(biāo)的默認(rèn)名稱是SQL
  • SELECT或DML操作產(chǎn)生隱式游標(biāo):
  • 隱式游標(biāo)的屬性值始終是最新執(zhí)行的SQL語(yǔ)句的。
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('產(chǎn)品名稱:' || cur_productname || ', 產(chǎn)品價(jià)格:' || cur_productprice);
    END IF ;
END;
/

輸出:

產(chǎn)品名稱:華為Mate40Pro, 產(chǎn)品價(jià)格:7999

PL/SQL 過(guò)程已成功完成。

3.2 游標(biāo)中使用異常處理

使用游標(biāo)時(shí),某些情況下得到的數(shù)據(jù)超出了控制范圍,如果不加處理會(huì)出現(xiàn)腳本執(zhí)行中斷的情況。這種情況下,腳本開(kāi)發(fā)者通常會(huì)使用異常處理來(lái)維護(hù)腳本的穩(wěn)定性。

??:在游標(biāo)中使用異常處理

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('產(chǎn)品名稱:' || cur_productname || ' ,產(chǎn)品價(jià)格:' || cur_productprice);
    END IF;
EXCEPTION
  WHEN NO_DATA_FOUND THEN
    DBMS_OUTPUT.PUT_LINE('沒(méi)有數(shù)據(jù)!');
  WHEN TOO_MANY_ROWS THEN
    DBMS_OUTPUT.PUT_LINE('數(shù)據(jù)過(guò)多!');
END;
/

輸出:

產(chǎn)品名稱:華為Mate40Pro ,產(chǎn)品價(jià)格:7999

PL/SQL 過(guò)程已成功完成。

4. 游標(biāo)案例

本案例涉及到兩張表,分別是EMP(員工表)和DEPT(部門表)。

要求:

利用游標(biāo)轉(zhuǎn)換這兩張表的數(shù)據(jù),要求把月薪高于1500的,部門地址為“CHICAGO”和“NEW YORK”的員工放到EMP_TEMP表中,月薪低于2500的上調(diào)5%。

分析:

第一步:建表 EMP_TEMP

第二步:創(chuàng)建游標(biāo)存儲(chǔ)符合條件的數(shù)據(jù)。

第三步:把符合要求的數(shù)據(jù)放進(jìn)新表

第四步:把工資上調(diào)

操作步驟:

建表

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

編寫游標(biāo)案例:

declare 
    cur_empno emp.empno%type;
    cur_ename emp.ename%type;
    cur_emp_cd emp%rowtype;
    
    --創(chuàng)建游標(biāo)存儲(chǔ)符合條件的數(shù)據(jù)
    cursor cur_emp is
        select * from emp where sal>1500 and DEPTNO 
        in (select DEPTNO from dept where loc in ('CHICAGO','NEW YORK'));
begin   

    --把符合條件的數(shù)據(jù)存到表中
    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('已經(jīng)取出所有數(shù)據(jù)!共' || cur_emp%rowcount || '條記錄');
        exit;
      end if;
    end loop;
    commit;
    close cur_emp;
    
    --調(diào)整工資
    update emp_temp 
    set sal = sal * 1.05
    where sal < 2500;
    
    dbms_output.put_line('工資調(diào)整完畢!共調(diào)整' || SQL%ROWCOUNT || '條記錄');
    commit;
end;
/

輸出:

已經(jīng)取出所有數(shù)據(jù)!共4條記錄
工資調(diào)整完畢!共調(diào)整2條記錄

PL/SQL 過(guò)程已成功完成。

習(xí)題

一、填空題

1.靜態(tài)游標(biāo)包含 ( )和( ) 兩種類型。
2.通常使用( )命令遍歷游標(biāo)的數(shù)據(jù)集。
3.游標(biāo)的使用步驟包括 ( )、( )、( )、( )
4.游標(biāo)4個(gè)屬性是 ( )、( )、( )、( )

二、選擇題

  1. 如果游標(biāo)沒(méi)有被打開(kāi)時(shí)就調(diào)用,會(huì)不會(huì)提示出錯(cuò)?( )
    A.不會(huì) B.會(huì)
  2. 下面有關(guān)隱式游標(biāo)中的%ISOPEN屬性,說(shuō)法正確的是( ).
    A. 隱式游標(biāo)中的%ISOPEN屬性永遠(yuǎn)返回FALSE
    B. 隱式游標(biāo)中的%ISOPEN屬性可以被人為地控制
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 在 PL/SQL 程序中,對(duì)于處理多行記錄的事務(wù)經(jīng)常使用游標(biāo)來(lái)實(shí)現(xiàn)。 游標(biāo)概念 為了處理 SQL 語(yǔ)句,ORACL...
    遼A丶孫悟空閱讀 643評(píng)論 0 15
  • 前言 厚積而薄發(fā)。 在 PL/SQL 程序中,對(duì)于處理多行記錄的事務(wù)經(jīng)常使用游標(biāo)來(lái)實(shí)現(xiàn)。 游標(biāo)的概念 --為了處理...
    olaH閱讀 1,193評(píng)論 0 4
  • SQL SERVER 參考: 游標(biāo)(Cursor)的講解與實(shí)例 在數(shù)據(jù)庫(kù)中,游標(biāo)是一個(gè)十分重要的概念。游標(biāo)提供了一...
    1edee8a22fc9閱讀 426評(píng)論 0 0
  • 一、簡(jiǎn)介 1、游標(biāo)的概念 游標(biāo)(Cursor) 就是一個(gè)變動(dòng)的光標(biāo),它本質(zhì)上是一個(gè)指針,指向從數(shù)據(jù)庫(kù)查詢出來(lái)的結(jié)果...
    滴滴滴9527閱讀 2,218評(píng)論 0 1
  • 2019-05-13 游標(biāo)(cursor)能夠根據(jù)查詢條件從數(shù)據(jù)表中提取一組記錄,將其作為一個(gè)臨時(shí)表置于數(shù)據(jù)緩沖區(qū)...
    Mr_J316閱讀 4,284評(píng)論 0 0

友情鏈接更多精彩內(nèi)容