小青蛙oracle跟踪,Oracle 存储过程:游标
一、認識游標
什么是游標?游標是數據庫的一種數據類型,它用來管理從數據源(表,視圖等)獲取到的數據結果集,可以想象成一個游動的光標(指針),指向一個結果集,通過游標的移動逐行提取每一行的記錄,就像我們屏幕上的光標指示當前位置一樣,“游標”由此得名。
游標分成靜態(tài)游標和動態(tài)游標(也叫REF游標)。
靜態(tài)游標:所謂靜態(tài)游標,顧名思義,指的是數據已經固定的游標,在使用游標前,已經知道游標中的數據和類型。靜態(tài)游標又可以細分成顯式游標和隱式游標,顯示游標指的是已經定義在變量區(qū),并且已經指定結果集的游標;隱式游標則是不用定義,直接就可以用的游標,比如系統(tǒng)定義的隱式游標sql,比如使用for循環(huán)遍歷某個SQL(這個SQL的結果集就是一個隱式游標)的自定義游標。
動態(tài)游標:與靜態(tài)游標相反,在定義的時候并不知道其結果集,在使用時,再給它定義結果集(通俗來說,就是查詢數據的SQL不是一成不變的)的游標。動態(tài)游標也可以細分成強類型和弱類型游標,強類型游標規(guī)定了其返回類型,弱類型游標則是不規(guī)定返回類型,可以獲取任何結果集。
在使用游標時,通常需要借助游標的一些屬性來做邏輯判斷,比如說判斷游標是否已經到了結果集的尾部,這個時候可以使用游標的found屬性來做判斷:if 游標%found then 。。以下是游標的一些屬性具體說明:
1.%found?:用于檢驗游標是否成功,通常在fetch語句前使用,當游標按照條件查詢一條記錄是,返回true。fetch語句(獲取記錄)執(zhí)行情況True or False。
2.%notfound :?最后一條記錄是否提取出true or false。?到了游標尾部,沒有記錄了,就返回true
3.%isopen?: 游標是否打開true or false。
4.%rowcount?:游標當前提取的行數?,即獲得影響的行數。
二、游標使用的語法
1.靜態(tài)游標語法(顯式):
a.聲明游標:劃分存儲區(qū)域,注意此時并沒有執(zhí)行Select?語句:
CURSOR?游標名(參數 列表) ??[返回值類型]? ?is? select?語句;
b.打開游標:執(zhí)行select?語句,獲得結果集存儲到游標中,此時游標指向結果集頭部,類似于java的迭代器,必須先執(zhí)行.next(),游標才指向第一條記錄。
open?游標名(參數 列表);
c.獲取記錄:移動游標取一條記錄:
fetch ?游標名 into ?臨時記錄或屬性類型變量;
d.關閉游標:將游標放入緩沖池中,沒有完全釋放資源。可重新打開。
close ?游標名;
2.動態(tài)游標語法:
a.聲明REF游標類型:這個聲明相當于自定義一個游標類型,在聲明REF游標類型時,可以一并確定REF?游標的分類:
⑴強類型REF游標:指定retrun type,REF?游標變量的類型必須和return type一致。
語法:type ??REF游標名? ?is? ?ref cursor return ?結果集返回記錄類型;
⑵弱類型REF游標:不指定return type,能和任何類型的CURSOR變量匹配,用于獲取任何結果集。
語法:type ??REF游標名?is? ?ref cursor;
b.聲明REF游標類型變量:
語法:變量名??已聲明Ref游標類型;
c.打開REF游標,關聯(lián)結果集:
語法:open ??REF 游標類型變量 ??for ??查詢語句返回結果集;
d.獲取記錄,操作記錄:
語法:fetch ???REF游標名 into? ?臨時記錄類型變量或屬性類型變量列表;
e.關閉游標,完全釋放資源:
語法:close? ?REF游標名;
3.游標的遍歷:
a.for循環(huán)游標:使用for循環(huán)遍歷游標時,會自動打開游標,并且循環(huán)結束會自動關閉游標,所以在for循環(huán)之前和之后都不需要對游標進行open、close操作。另外,緊跟著for關鍵字的變量是不需要提前定義的。語法:
for 變量名 in 游標名
loop
處理邏輯;
end loop;
b.loop循環(huán)游標:? ?loop循環(huán)是不會自動打開或者關閉游標的,需要手動操作。退出循環(huán)語句必須在執(zhí)行邏輯操作之前執(zhí)行,原因是因為即使游標已經遍歷完,已經記錄游標變量的記錄是不會清除的,如果先執(zhí)行邏輯操作,會導致循環(huán)多走一次。語法:
open 游標名;
loop
fetch ?游標名 into ?臨時記錄或屬性類型變量(多個以逗號隔開);
exit ?when ??游標名%notfound;
邏輯操作
end ??loop;
close 游標名;
c.while循環(huán)游標:和loop有一點類似,語法:
open 游標名;
fetch ?游標名 into 臨時記錄或屬性類型變量(多個以逗號隔開);
while 游標名%found
loop
邏輯處理;
fetch ?游標名 into 臨時記錄或屬性類型變量(多個以逗號隔開);
end loop;
close?游標名;
三、示例
以下寫了兩個存儲過程,分別記錄了靜態(tài)游標和動態(tài)游標的基礎用法,可以用作參考:
靜態(tài)游標相關:
create or replace procedure test_static_cursor is
--無參數靜態(tài)顯式游標
-- return test_user%rowtype 這里的返回值可以要 也可以不要,因為后面的SQL已經指定了返回值
CURSOR static_cursor return test_user%rowtype is
select * /**u.id, u.username, u.password*/
from test_user u;
--帶參數的顯示游標 (參數名 參數類型 [default 默認值])
CURSOR static_cursor1(p_name test_user.id%type default '123') is
select * from test_user u where u.id = p_name;
--定義變量 這里的變量類型的意思是保持和test_user的id列的類型一致.
--在定義變量以獲取游標的數據時,建議使用這種方式
v_id test_user.id%type;
v_username test_user.username%type;
v_password varchar2(32);
--定義記錄(記錄的意思是游標的一條記錄)變量
v_record static_cursor1%rowtype;
v_num number;
begin
--初始化一些數據
delete from test_user;
commit;
select count(1) into v_num from test_user;
if v_num = 0 then
insert into test_user
(id, username, password)
values
('123', 'shaoyu', 'shaoyu');
--系統(tǒng)定義的隱式游標:SQL
--注意一句sql語句只會影響一個隱式游標,多個sql語句執(zhí)行會覆蓋隱式游標sql
if sql%found then
dbms_output.put_line('成功插入' || sql%rowcount || '條數據');
end if;
insert into test_user
(id, username, password)
values
('456', 'admin', 'admin');
insert into test_user
(id, username, password)
values
('789', 'system', 'system');
commit;
end if;
--打開游標,此時會執(zhí)行定義游標時的SQL
open static_cursor;
--讀取游標數據
fetch static_cursor
into v_id, v_username, v_password;
--驗證
dbms_output.put_line(v_id || '-' || v_username || '-' || v_password);
--關閉游標
close static_cursor;
--打開游標
open static_cursor1('456');
--讀取游標數據存入單個變量
fetch static_cursor1
into v_id, v_username, v_password;
--驗證
dbms_output.put_line(v_id || '-' || v_username || '-' || v_password);
close static_cursor1;
open static_cursor1('789');
--讀取游標數據存入記錄變量
fetch static_cursor1 into v_record ;
--驗證
dbms_output.put_line(v_record.id || '-' || v_record.username || '-' || v_record.password);
close static_cursor1;
--游標的遍歷:
--1.for循環(huán)(不需要打開游標)
dbms_output.put_line('for循環(huán)');
if static_cursor%isopen then
dbms_output.put_line('游標已打開');
else
dbms_output.put_line('游標未打開');
end if;
--data不需要提前定義
for data in static_cursor loop
if static_cursor%isopen then
dbms_output.put_line('游標已打開');
else
dbms_output.put_line('游標未打開');
end if;
dbms_output.put_line(data.id || '-' || data.username || '-' ||
data.password);
end loop;
if static_cursor%isopen then
dbms_output.put_line('游標已打開');
else
dbms_output.put_line('游標未打開');
end if;
--2.loop循環(huán)
dbms_output.put_line('loop循環(huán)');
open static_cursor;
loop
fetch static_cursor
into v_id, v_username, v_password;
exit when static_cursor%notfound;
dbms_output.put_line(v_id || '-' || v_username || '-' || v_password);
end loop;
close static_cursor;
--3.while循環(huán)
dbms_output.put_line('while循環(huán)');
open static_cursor;
fetch static_cursor
into v_id, v_username, v_password;
while static_cursor%found loop
dbms_output.put_line(v_id || '-' || v_username || '-' || v_password);
fetch static_cursor
into v_id, v_username, v_password;
end loop;
close static_cursor;
end test_static_cursor;
動態(tài)游標:
create or replace procedure test_dynamic_cursor is
--定義強類型REF游標類型
type dynamic_cursor_type1 is ref cursor return test_user%rowtype;
--定義弱類型REF游標
type dynamic_cursor_type2 is ref cursor;
--定義強類型REF自定義返回記錄類型游標類型 先定義自定義返回記錄類型 再定義游標類型
type dynamic_cursor_type3_rec is record(
user_id test_user.id%type,
username test_user.username%type);
type dynamic_cursor_type3 is ref cursor return dynamic_cursor_type3_rec;
--定義之前定義好的游標類型
dynamic_cursor1 dynamic_cursor_type1;
dynamic_cursor2 dynamic_cursor_type2;
dynamic_cursor3 dynamic_cursor_type3;
--定義返回類型變量
rec3 dynamic_cursor_type3_rec;
--定義變量
v_id test_user.id%type;
v_username test_user.username%type;
v_password varchar2(32);
v_num number;
begin
--初始化一些數據
delete from test_user;
commit;
select count(1) into v_num from test_user;
if v_num = 0 then
insert into test_user
(id, username, password)
values
('123', 'shaoyu', 'shaoyu');
insert into test_user
(id, username, password)
values
('456', 'admin', 'admin');
insert into test_user
(id, username, password)
values
('789', 'system', 'system');
commit;
end if;
dbms_output.put_line('強類型動態(tài)游標');
--給強類型動態(tài)游標關聯(lián)結果集
open dynamic_cursor1 for select * from test_user;
--驗證
loop
fetch dynamic_cursor1
into v_id, v_username, v_password;
exit when dynamic_cursor1%notfound;
dbms_output.put_line(v_id || '-' || v_username || '-' || v_password);
end loop;
close dynamic_cursor1;
--給弱類型動態(tài)游標關聯(lián)結果集
dbms_output.put_line('弱類型動態(tài)游標');
open dynamic_cursor2 for select id,password from test_user;
--驗證
loop
fetch dynamic_cursor2
into v_id, v_password;
exit when dynamic_cursor2%notfound;
dbms_output.put_line(v_id || '-' || v_password);
end loop;
close dynamic_cursor2;
--給自定義強類型動態(tài)游標關聯(lián)結果集
dbms_output.put_line('自定義返回類型強類型動態(tài)游標');
open dynamic_cursor3 for select id,username from test_user;
--驗證
loop
fetch dynamic_cursor3
into rec3;
exit when dynamic_cursor3%notfound;
dbms_output.put_line(rec3.user_id || '-' || rec3.username);
end loop;
close dynamic_cursor3;
end test_dynamic_cursor;
以上看起來游標好像就這么一些用法,那還有沒有別的用法呢?有的,那就是在使用游標時,對游標的結果集對應的數據源進行操作。
四、更新、刪除游標記錄
在定義游標的時候,如果在定義結果集的語句后面加上for update或者for delete子串,那么在使用游標時,就可以對游標的結果集進行操作,不要擔心數據源的狀態(tài),當使用for update、for delete子串打開一個游標時,所有返回集中的數據行都將處于行級(ROW-LEVEL)獨占式鎖定,其他對象只能查詢這些數據行,不能進行update、delete或select...for update操作,保證了數據的正確性。
值得提醒的是,在多表查詢中,使用of子句來鎖定特定的表,如果忽略了of子句,那么所有表中選擇的數據行都將被鎖定。如果這些數據行已經被其他會話鎖定,那么正常情況下oracle將等待,直到數據行解鎖。
語法:
a.聲明更新或刪除顯示游標:
cursor?游標名 is? select?語句 ??for update [ of ?更新列列名];
cursor?游標名 is? select?語句 ??for delete [ of ?更新列列名];
b.使用顯示游標當前記錄來更新或刪除:
update 表名 ??set 更新語句 ?where ??current ?of ??游標名;
delete from 表名 ?where ??current ?of ??游標名;
這個就不寫例子了,第三步的示例理解了之后,這個很容易編寫。
五、使用游標作為存儲過程出參
說了這么多,并沒有將游標應用到實際中,其實web程序對數據庫的調用多數情況下需要返回一個結果集,很顯然,游標是非常適合的。在這種情況下,只需要將游標作為存儲過程的出參就可以了。
1.包的概念
在上一篇中提到了包和存儲過程,那什么是包呢?包(package)也是數據庫的一種對象類型,它包含定義和包體(body)兩個方面,【定義】類似于是java中的接口,【包體】類似于是java中對接口的實現類,包里面是可以包含【自定義類型】和【存儲過程】的,可以認為是java接口中的全局變量(自定義類型)和方法(存儲過程),就連使用方式也極其類似:包名.存儲過程名(參數)。
有人覺得奇怪,不是要說游標做為存儲過程出參嗎?怎么又扯上包這個東西了?
在java中,所有的變量都有一個作用域,oracle數據庫也不例外,假設我們單獨定義一個存儲過程,在參數那一列是要規(guī)定參數類型的,如果我們使用的是自定義的游標,那么這個游標類型在這個存儲過程參數里是肯定沒有定義的,所以我們需要借助包,在包中定義自定義的游標類型,然后再把這個自定義游標作為包中的存儲過程的出入參,這樣就保證了游標在存儲過程中的作用域始終可用。
2.包的語法:
包定義:
create or replace package 包名 as
定義 自定義type
定義 全局變量
procedure 存儲過程名; --沒有存儲過程具體實現
function 函數名;
end test_package;
包體定義:
create or replace package body test_package as
定義變量
procedure 存儲過程名(參數) is ...存儲過程具體實現
end test_package;
下面寫個實例:
create or replace package test_package as
--定義游標類型
type o_cur is ref cursor;
--定義存儲過程
procedure test_static_cursor(o_data out o_cur);
end test_package;
create or replace package body test_package as
--存儲過程具體實現
procedure test_static_cursor(o_data out o_cur) is
v_num number;
begin
--初始化一些數據
delete from test_user;
commit;
select count(1) into v_num from test_user;
if v_num = 0 then
insert into test_user
(id, username, password)
values
('123', 'shaoyu', 'shaoyu');
insert into test_user
(id, username, password)
values
('456', 'admin', 'admin');
insert into test_user
(id, username, password)
values
('789', 'system', 'system');
commit;
end if;
--給出參關聯(lián)結果集
open o_data for
select * from test_user;
end;
end test_package;
總結
以上是生活随笔為你收集整理的小青蛙oracle跟踪,Oracle 存储过程:游标的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java判断输入月份_Java输入年份和
- 下一篇: 鸿蒙唯独没有手机,想用鸿蒙OS,却没有华