第四章 游標(biāo)的使用

在 PL/SQL 程序中县习,對(duì)于處理多行記錄的事務(wù)經(jīng)常使用游標(biāo)來(lái)實(shí)現(xiàn)浸须。

游標(biāo)概念

為了處理 SQL 語(yǔ)句简十,ORACLE 必須分配一片叫上下文( context area )的區(qū)域來(lái)處理所必需的信息硕勿,其中 包括要處理的行的數(shù)目鄙早,一個(gè)指向語(yǔ)句被分析以后的表示形式的指針以及查詢的活動(dòng)集(active set)汪茧。
游標(biāo)是一個(gè)指向上下文的句柄( handle)或指針。通過(guò)游標(biāo)限番,PL/SQL 可以控制上下文區(qū)和處理語(yǔ)句時(shí)上 下文區(qū)會(huì)發(fā)生些什么事情舱污。
對(duì)于不同的 SQL 語(yǔ)句,游標(biāo)的使用情況不同:

SQL語(yǔ)句 游標(biāo)
非查詢語(yǔ)句 隱式的
結(jié)果是單行的查詢語(yǔ)句 隱式的或顯示的
結(jié)果是多行的查詢語(yǔ)句 顯示的
  • 處理顯式游標(biāo)
  1. 顯式游標(biāo)處理
    顯式游標(biāo)處理需四個(gè) PL/SQL 步驟:
  • 定義游標(biāo):就是定義一個(gè)游標(biāo)名扳缕,以及與其相對(duì)應(yīng)的 SELECT 語(yǔ)句慌闭。
    格式:
    CURSOR cursor_name[(parameter[, parameter]…)] IS select_statement;
    游標(biāo)參數(shù)只能為輸入?yún)?shù),其格式為:
    parameter_name [IN] datatype [{:= | DEFAULT} expression]
    在指定數(shù)據(jù)類(lèi)型時(shí)躯舔,不能使用長(zhǎng)度約束驴剔。如 NUMBER(4)、CHAR(10) 等都是錯(cuò)誤的粥庄。
  • 打開(kāi)游標(biāo):就是執(zhí)行游標(biāo)所對(duì)應(yīng)的 SELECT 語(yǔ)句丧失,將其查詢結(jié)果放入工作區(qū),并且指針指向工作區(qū)的首 部惜互,標(biāo)識(shí)游標(biāo)結(jié)果集合布讹。如果游標(biāo)查詢語(yǔ)句中帶有 FOR UPDATE 選項(xiàng),OPEN 語(yǔ)句還將鎖定數(shù)據(jù)庫(kù)表中 游標(biāo)結(jié)果集合對(duì)應(yīng)的數(shù)據(jù)行训堆。
    格式:
    OPEN cursor_name[([parameter =>] value[, [parameter =>] value]…)];
    在向游標(biāo)傳遞參數(shù)時(shí)描验,可以使用與函數(shù)參數(shù)相同的傳值方法,即位置表示法和名稱(chēng)表示法坑鱼。PL/SQL 程序不能用 OPEN 語(yǔ)句重復(fù)打開(kāi)一個(gè)游標(biāo)膘流。
  • 提取游標(biāo)數(shù)據(jù):就是檢索結(jié)果集合中的數(shù)據(jù)行,放入指定的輸出變量中鲁沥。
    格式:
    FETCH cursor_name INTO {variable_list | record_variable };
  • 對(duì)該記錄進(jìn)行處理呼股;
  • 繼續(xù)處理,直到活動(dòng)集合中沒(méi)有記錄画恰;
  • 關(guān)閉游標(biāo):
    當(dāng)提取和處理完游標(biāo)結(jié)果集合數(shù)據(jù)后彭谁,應(yīng)及時(shí)關(guān)閉游標(biāo),以釋放該游標(biāo)所占用的系統(tǒng)資源允扇, 并使該游標(biāo)的工作區(qū)變成無(wú)效缠局,不能再使用 FETCH 語(yǔ)句取其中數(shù)據(jù)则奥。關(guān)閉后的游標(biāo)可以使用 OPEN 語(yǔ) 句重新打開(kāi)。
    格式:
    CLOSE cursor_name;
    注:定義的游標(biāo)不能有 INTO 子句甩鳄。

例 1. 查詢前 10 名員工的信息逞度。

declare
   --定義游標(biāo)
   cursor c_cursor is select last_name,salary
                      from employees
                      where rownum < 11
                     order by salary;
  v_name employees.last_name%type;
  v_sal employees.salary%type;
begin
  --打開(kāi)游標(biāo)
  open c_cursor;
  --提取游標(biāo)數(shù)據(jù)
  fetch c_cursor into v_name,v_sal;
  
  while c_cursor % found loop
        dbms_output.put_line(v_name || ':' || v_sal);
        fetch c_cursor into v_name,v_sal;
  end loop;
  --關(guān)閉游標(biāo)
  close c_cursor;
end;

例 2. 游標(biāo)參數(shù)的傳遞方法。

declare
   --定義游標(biāo)
   cursor c_cursor(emp_no number default 11) is select last_name,salary
                      from employees
                      where rownum < emp_no
                     order by salary;
  v_name employees.last_name%type;
  v_sal employees.salary%type;
begin
  --打開(kāi)游標(biāo)
  open c_cursor;
  --提取游標(biāo)數(shù)據(jù)
  fetch c_cursor into v_name,v_sal;
  
  while c_cursor % found loop
        dbms_output.put_line(v_name || ':' || v_sal);
        fetch c_cursor into v_name,v_sal;
  end loop;
  --關(guān)閉游標(biāo)
  close c_cursor;
end;
declare
   --定義游標(biāo)
   cursor c_cursor(emp_no number default 11) is select last_name,salary
                      from employees
                      where rownum < emp_no
                     order by salary;
  v_name employees.last_name%type;
  v_sal employees.salary%type;
begin
  --打開(kāi)游標(biāo)
  open c_cursor(emp_no => 20);
  --提取游標(biāo)數(shù)據(jù)
  fetch c_cursor into v_name,v_sal;
  
  while c_cursor % found loop
        dbms_output.put_line(v_name || ':' || v_sal);
        fetch c_cursor into v_name,v_sal;
  end loop;
  --關(guān)閉游標(biāo)
  close c_cursor;
end;
  1. 游標(biāo)屬性
    %FOUND 布爾型屬性妙啃,當(dāng)最近一次讀記錄時(shí)成功返回,則值為 TRUE档泽; %NOTFOUND 布爾型屬性,與%FOUND 相反揖赴;
    %ISOPEN 布爾型屬性馆匿,當(dāng)游標(biāo)已打開(kāi)時(shí)返回 TRUE;
    %ROWCOUNT 數(shù)字型屬性燥滑,返回已從游標(biāo)中讀取的記錄數(shù)渐北。
  • 例 3:給工資低于 3000 的員工工資調(diào)為 3000。
declare
  v_eid employees.employee_id%type;
  v_sal employees.salary%type;
  cursor c_cursor is select employee_id,salary
                     from employees;
begin
  open c_cursor;
  loop
      fetch c_cursor into v_eid,v_sal;
      exit when c_cursor %notfound;
      if v_sal <= 3000 then
          update employees set salary = 3000;
          where employee_id = v_eid;
          dbms_output.put_line('員工:' || v_eid || '工資已經(jīng)更新');
      end if;
  end loop;
  dbms_output.put_line('記錄數(shù)為:' || c_cursor %rowcount);
  close c_cursor;
end;
  1. 游標(biāo)的 FOR 循環(huán)
    PL/SQL 語(yǔ)言提供了游標(biāo) FOR 循環(huán)語(yǔ)句铭拧,自動(dòng)執(zhí)行游標(biāo)的 OPEN赃蛛、FETCH、CLOSE 語(yǔ)句和循環(huán)語(yǔ)句的功能搀菩; 當(dāng)進(jìn)入循環(huán)時(shí)呕臂,游標(biāo) FOR 循環(huán)語(yǔ)句自動(dòng)打開(kāi)游標(biāo),并提取第一行游標(biāo)數(shù)據(jù)肪跋,當(dāng)程序處理完當(dāng)前所提取的數(shù) 據(jù)而進(jìn)入下一次循環(huán)時(shí)歧蒋,游標(biāo) FOR 循環(huán)語(yǔ)句自動(dòng)提取下一行數(shù)據(jù)供程序處理,當(dāng)提取完結(jié)果集合中的所有 數(shù)據(jù)行后結(jié)束循環(huán)州既,并自動(dòng)關(guān)閉游標(biāo)谜洽。
    格式:
    FOR index_variable IN cursor_name[value[, value]…] LOOP
    -- 游標(biāo)數(shù)據(jù)處理代碼
    END LOOP;
  • 其中: index_variable 為游標(biāo) FOR 循環(huán)語(yǔ)句隱含聲明的索引變量,該變量為記錄變量吴叶,其結(jié)構(gòu)與游標(biāo)查詢語(yǔ)句 返回的結(jié)構(gòu)集合的結(jié)構(gòu)相同阐虚。在程序中可以通過(guò)引用該索引記錄變量元素來(lái)讀取所提取的游標(biāo)數(shù)據(jù), index_variable 中各元素的名稱(chēng)與游標(biāo)查詢語(yǔ)句選擇列表中所制定的列名相同蚌卤。如果在游標(biāo)查詢語(yǔ)句的選擇 列表中存在計(jì)算列敌呈,則必須為這些計(jì)算列指定別名后才能通過(guò)游標(biāo) FOR 循環(huán)語(yǔ)句中的索引變量來(lái)訪問(wèn)這些 列數(shù)據(jù)。
  • 注:不要在程序中對(duì)游標(biāo)進(jìn)行人工操作造寝;不要在程序中定義用于控制 FOR 循環(huán)的記錄
  • 例4:
declare
  cursor c_emp is select last_name,salary sal
                  from employees;
begin
  for v_emp in c_emp loop
      dbms_output.put_line(v_emp.last_name || ',' || v_emp.sal);
  end loop;
end;
  • 例 5:當(dāng)所聲明的游標(biāo)帶有參數(shù)時(shí),通過(guò)游標(biāo) FOR 循環(huán)語(yǔ)句為游標(biāo)傳遞參數(shù)吭练。
declare
  cursor c_emp(dep_id number default 50) is 
                  select last_name,salary sal
                  from employees
                  where department_id = dep_id;
begin
  for v_emp in c_emp loop
      dbms_output.put_line(v_emp.last_name || ',' || v_emp.sal);
  end loop;
end;
declare
  cursor c_emp(dep_id number default 50) is 
                  select last_name,salary sal
                  from employees
                  where department_id = dep_id;
begin
  for v_emp in c_emp(80) loop
      dbms_output.put_line(v_emp.last_name || ',' || v_emp.sal);
  end loop;
end;
  • 例 6:PL/SQL 還允許在游標(biāo) FOR 循環(huán)語(yǔ)句中使用子查詢來(lái)實(shí)現(xiàn)游標(biāo)的功能诫龙。
begin
  for v_emp in(select last_name,salary from employees) loop
      dbms_output.put_line(v_emp.last_name || ',' || v_emp.salary);
  end loop;
end;
  • 處理隱式游標(biāo)

顯式游標(biāo)主要是用于對(duì)查詢語(yǔ)句的處理,尤其是在查詢結(jié)果為多條記錄的情況下鲫咽;而對(duì)于非查詢語(yǔ)句签赃, 如修改谷异、刪除操作,則由 ORACLE 系統(tǒng)自動(dòng)地為這些操作設(shè)置游標(biāo)并創(chuàng)建其工作區(qū)锦聊,這些由系統(tǒng)隱含創(chuàng)建 的游標(biāo)稱(chēng)為隱式游標(biāo)歹嘹,隱式游標(biāo)的名字為 SQL,這是由 ORACLE 系統(tǒng)定義的孔庭。對(duì)于隱式游標(biāo)的操作尺上,如定義、 打開(kāi)圆到、取值及關(guān)閉操作怎抛,都由 ORACLE 系統(tǒng)自動(dòng)地完成,無(wú)需用戶進(jìn)行處理芽淡。用戶只能通過(guò)隱式游標(biāo)的相 關(guān)屬性马绝,來(lái)完成相應(yīng)的操作。在隱式游標(biāo)的工作區(qū)中挣菲,所存放的數(shù)據(jù)是與用戶自定義的顯示游標(biāo)無(wú)關(guān)的富稻、 最新處理的一條 SQL 語(yǔ)句所包含的數(shù)據(jù)。

  • 格式調(diào)用為: SQL%
  • 隱式游標(biāo)屬性
    SQL%FOUND 布爾型屬性,當(dāng)最近一次讀記錄時(shí)成功返回白胀,則值為 TRUE椭赋;
    SQL%NOTFOUND 布爾型屬性,與%FOUND 相反;
    SQL%ROWCOUNT 數(shù)字型屬性, 返回已從游標(biāo)中讀取得記錄數(shù)纹笼;
    SQL%ISOPEN 布爾型屬性, 取值總是 FALSE纹份。SQL 命令執(zhí)行完畢立即關(guān)閉隱式游標(biāo)。
  • 例 7: 更新指定員工信息廷痘,如果該員工沒(méi)有找到蔓涧,則打印”查無(wú)此人”信息。
declare
  v_name employees.last_name%type;
  v_id employees.employee_id%type := &v_id;
begin
  update employees
  set last_name = 'xx'
  where employee_id = v_id;
  
  if SQL%NOTFOUND then 
      dbms_output.put_line('查無(wú)此人');
  end if;
end;
  • 關(guān)于 NO_DATA_FOUND 和 %NOTFOUND 的區(qū)別
  • SELECT … INTO 語(yǔ)句觸發(fā) NO_DATA_FOUND笋额;
  • 當(dāng)一個(gè)顯式游標(biāo)的 WHERE 子句未找到時(shí)觸發(fā)%NOTFOUND元暴;
  • 當(dāng) UPDATE 或 DELETE 語(yǔ)句的 WHERE 子句未找到時(shí)觸發(fā)
    SQL%NOTFOUND;在提取循環(huán)中要用 %NOTFOUND 或%FOUND 來(lái)確定循環(huán)的退出條件兄猩,不要用 NO_DATA_FOUND.
  • 游標(biāo)修改和刪除操作

游標(biāo)修改和刪除操作是指在游標(biāo)定位下茉盏,修改或刪除表中指定的數(shù)據(jù)行。這時(shí)枢冤,要求游標(biāo)查詢語(yǔ)句中 必須使用 FOR UPDATE 選項(xiàng)鸠姨,以便在打開(kāi)游標(biāo)時(shí)鎖定游標(biāo)結(jié)果集合在表中對(duì)應(yīng)數(shù)據(jù)行的所有列和部分列。
為了對(duì)正在處理(查詢)的行不被另外的用戶改動(dòng)淹真,ORACLE 提供一個(gè) FOR UPDATE 子句來(lái)對(duì)所選擇的行 進(jìn)行鎖住讶迁。該需求迫使 ORACLE 鎖定游標(biāo)結(jié)果集合的行,可以防止其他事務(wù)處理更新或刪除相同的行核蘸,直到 您的事務(wù)處理提交或回退為止巍糯。
語(yǔ)法:
SELECT . . . FROM … FOR UPDATE [OF column[, column]…] [NOWAIT]

  • 如果另一個(gè)會(huì)話已對(duì)活動(dòng)集中的行加了鎖啸驯,那么 SELECT FOR UPDATE 操作一直等待到其它的會(huì)話釋放這 些鎖后才繼續(xù)自己的操作,對(duì)于這種情況祟峦,當(dāng)加上 NOWAIT 子句時(shí)罚斗,如果這些行真的被另一個(gè)會(huì)話鎖定, 則 OPEN 立即返回并給出: ORA-0054 :resource busy and acquire with nowait specified.
  • 如果使用 FOR UPDATE 聲明游標(biāo)宅楞,則可在 DELETE 和 UPDATE 語(yǔ)句中使用 WHERE CURRENT OF cursor_name 子句针姿,修改或刪除游標(biāo)結(jié)果集合當(dāng)前行對(duì)應(yīng)的數(shù)據(jù)庫(kù)表中的數(shù)據(jù)行。
  • 例 8:從 EMPLOYEES 表中查詢某部門(mén)的員工情況咱筛,將其工資最低定為 3000搓幌;
declare
  v_dep_id employees.department_id%type := &v_dep_id;
  cursor emp_cursor is
          select last_name,salary
          from employees
          where department_id = v_dep_id
          for update nowait;
begin
  for emp_rec in emp_cursor loop
      if emp_rec.salary < 3000 then
          update employees set salary = 3000
          where current of emp_cursor;
      end if;
  end loop;
end;
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市迅箩,隨后出現(xiàn)的幾起案子溉愁,更是在濱河造成了極大的恐慌,老刑警劉巖饲趋,帶你破解...
    沈念sama閱讀 212,454評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件拐揭,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡奕塑,警方通過(guò)查閱死者的電腦和手機(jī)堂污,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)龄砰,“玉大人盟猖,你說(shuō)我怎么就攤上這事』慌铮” “怎么了式镐?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,921評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)固蚤。 經(jīng)常有香客問(wèn)我娘汞,道長(zhǎng),這世上最難降的妖魔是什么夕玩? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,648評(píng)論 1 284
  • 正文 為了忘掉前任你弦,我火速辦了婚禮,結(jié)果婚禮上燎孟,老公的妹妹穿的比我還像新娘禽作。我一直安慰自己,他們只是感情好揩页,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,770評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布领迈。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪狸捅。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,950評(píng)論 1 291
  • 那天累提,我揣著相機(jī)與錄音尘喝,去河邊找鬼。 笑死斋陪,一個(gè)胖子當(dāng)著我的面吹牛朽褪,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播无虚,決...
    沈念sama閱讀 39,090評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼缔赠,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了友题?” 一聲冷哼從身側(cè)響起嗤堰,我...
    開(kāi)封第一講書(shū)人閱讀 37,817評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎度宦,沒(méi)想到半個(gè)月后踢匣,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,275評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡戈抄,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,592評(píng)論 2 327
  • 正文 我和宋清朗相戀三年离唬,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片划鸽。...
    茶點(diǎn)故事閱讀 38,724評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡输莺,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出裸诽,到底是詐尸還是另有隱情嫂用,我是刑警寧澤,帶...
    沈念sama閱讀 34,409評(píng)論 4 333
  • 正文 年R本政府宣布崭捍,位于F島的核電站尸折,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏殷蛇。R本人自食惡果不足惜实夹,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,052評(píng)論 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望粒梦。 院中可真熱鬧亮航,春花似錦、人聲如沸匀们。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,815評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至重抖,卻和暖如春露氮,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背钟沛。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,043評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工畔规, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人恨统。 一個(gè)月前我還...
    沈念sama閱讀 46,503評(píng)論 2 361
  • 正文 我出身青樓叁扫,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親畜埋。 傳聞我的和親對(duì)象是個(gè)殘疾皇子莫绣,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,627評(píng)論 2 350

推薦閱讀更多精彩內(nèi)容