Oracle存储过程+游标
一. 游標
PL/SQL 是用游標來管理 SQL 的 SELECT 語句的 . 游標是為了處理這些語句而分配的一大塊內存 . 它提供了對一個結果集進行逐行處理的能力 , 可看作是一種特殊的指針 . 它與某個查詢結果集相關聯 , 可以指向結果集的任意位置 , 以便對指定位置的數據進行處理 . 使用它可以在查詢數據的同時對數據進行處理.
二. 游標的屬性
1.%FOUND:判斷當前游標是否正確的指向第一行記錄,如果是則返回true,不是則返回false。
2.%NOTFOUND:與%FOUND相反
3.%ISOPEN:判斷游標是否打開,如果是的話,就返回true,不是則返回false.
4.%ROWCOUNT:判斷當前游標在所指向的結果集中的提取的行數,不是所有的記錄數.
SQL%FOUND和SQL%NOTFOUND
在執行任何DML語句前SQL%FOUND和SQL%NOTFOUND的值都是NULL,在執行DML語句后,SQL%FOUND的屬性值將是:
.TRUE:INSERT
.TRUE:DELETE和UPDATE,至少有一行被DELETE或UPDATE.
. TRUE:SELECTINTO至少返回一行
當SQL%FOUND為TRUE時,SQL%NOTFOUND為FALSE。
另外,有兩個變量屬性
1.%TYPE:聲明變量的類型與表中的某列的數據類型完全一致.
它的好處有:一.是你不必知道某列的確切數據類型.二:當某列的數據類型改變時不用修改變量的數據類型.
不但列名可以使用%TYPE,而且變量、游標、記錄,或聲明的常量都可以使用%TYPE.
2.%ROWTYPE:聲明變量的數據類型與表中的行記錄數據類型一致. 對于自定義的記錄, 則必須聲明自己的域.
三.游標的類型
1.靜態游標
靜態游標只是數據的一個快照,用戶對記錄所做的修改,增加或刪除記錄都不會反映到記錄集中。靜態游標 從不檢測其他的更新、刪除和插入情況。例如,一個靜態游標提取了一行,然后另一個應用程序更新了這一行。如果應用程序從該靜態游標再提取該行,它所看到的值并不發生改變,盡管另一個應用程序已經更改了這行的值。
靜態游標又分為兩種類型
① 顯式游標
顯式游標由用戶定義, 并由用戶來管理游標, 可返回多行記錄.
語法:
declare
Cursor cur(游標名) is select * from myuser;(sql語句);
begin
open cur --打開游標
Fetch cur into [變量名或其他數據類型];
close cur --關閉游標
end;
使用顯式游標一般按照如下規則
聲明游標--打開游標--讀取數據--關閉游標
注:FOR.. IN .. LOOP 語句已經包含OPEN,FETCH,CLOSE操作。所以除外。
例子1:
declare
no myuser.userid%TYPE ;
newuser myuser%ROWTYPE;
Cursor mycursor is select * from myuser;
begin
open mycursor;
loop
fetch mycursor into newuser; --fetch之后將移到下一行數據。
no :=newuser.userid;
if mycursor%NOTFOUND then --這個要放在fetch語句之后,不然會死循環
beginexit;end ;end if;
Dbms_Output.put_line(no);
end loop;
close mycursor;
end;
這是按照規則進行的步驟,下面用另一種方法for。。。in。。。loop。
例子2: for .... in .... loop 演示
declare
Cursor mycursor is select * from myuser;
begin
for newuser in mycursor loop -- 將每條記錄放到newuser 中
begin
Dbms_Output.put_line('UserName is '||newuser.username);
end;
end loop;
end;
例子3:利用帶參數的存儲過程演示
create or replace procedure myproc (username in myuser.username%TYPE)
as
userId myuser.userid%TYPE;
Cursor mycursor is select userid from myuser where username = username;
begin
open mycursor;
fetch mycursor into userId;--先獲取一行數據,游標將移到下一行
while mycursor%FOUND --此時這邊返回的才不是null,不會產生死循環
loop
Dbms_Output.put_line(username||'s userId is '||userId);
fetch mycursor into userId; --如果返回的是多個參數,則在userid后用逗號隔開加入。
end loop;
close mycursor;--關閉游標
end myproc;
例子4:帶參數的游標
declare
Cursor mycursor(username myuser.username%TYPE) is
select userId from myuser where username=username;
usId myuser.userid%TYPE;
begin
open mycursor('xiaomin'); --調用游標要給其參數
loop
fetch mycursor into usId;
exit when mycursor%NOTFOUND;-----當數據完成之后跳出循環
Dbms_Output.put_line('id is '||usId);
end loop;\
close mycursor;
end;
例子5. 游標for循環使用查詢
begin
for newuser in (select username from myuser) loop
begin
Dbms_Output.put_line('userName is ' || newuser.username);
end;
end loop;
end;
例子6. 游標中的子查詢
CURSORC1ISSELECT*FROMempWHERE deptno NOT IN (SELECT deptno FROM dept) WHERE dname! = 'ACCOUNTING');
可以看出與SQL中的子查詢沒有什么區別
②隱式游標
隱式游標的特點:
-隱式游標是由PL/SQL 來管理的, 即不需要聲明游標語句, 也不需要OPEN,FETCH,CLOSE 操作
-隱式游標中必須要有select cur_name into [ 變量名或其他數據類型]. 此句完成OPEN,FETCH,CLOSE 操作.
-隱式游標只能返回一行記錄, 如果無符合條件的記錄將會出現NO_DATA_FOUND 異常. 如果出現多條記錄將出現TOO_MANY_ROWS 異常.
-隱式游標只能用SQL% 判斷其游標屬性
-對于任何位置使用SQL%ISOPEN 結果都是FALSE, 隱式游標由PL/SQL 管理 對于隱式游標而言SQL%ISOPEN總是FALSE,這是因為隱式游標在DML語句執行時打開,結束時就立即關閉
-對于在隱式游標位置前使用SQL%FOUND 或SQL%NOTFOUND, SQL%ROWCOUNT 結果值是NULL( 不確定值)
例子:驗證隱式游標的屬性
declare
uname myuser.username%TYPE;
upass myuser.userpass%TYPE;
userid myuser.userid%TYPE;
begin
if sql%isopen then
-- 判斷游標是否打開
dbms_output.put_line('游標處于開啟狀態');
else
dbms_output.put_line('游標處于關閉狀態');
end if;
if sql%notfound then
-- 判斷游標是否指向有效的行
dbms_output.put_line('游標沒有指向有效行');
else
dbms_output.put_line('游標指向有效行');
end if;
dbms_output.put_line(sql%rowcount);
dbms_output.put_line('---------------');
/** 去掉where 條件時, 將會出現too_many_rows 異常**/
select userid, username, userpass
into userid, uname, upass
from myuser
where userid = 1; -- 隱式游標必-- 須使用INTO
dbms_output.put_line(sql%rowcount);
if sql%isopen then
-- 判斷游標是否打開
dbms_output.put_line('游標處于開啟狀態');
else
dbms_output.put_line('游標處于關閉狀態');
end if;
if sql%notfound then
-- 判斷游標是否指向有效的行
dbms_output.put_line('游標沒有指向有效行');
else
dbms_output.put_line('游標指向有效行');
end if;
exception
when no_data_found then
dbms_output.put_line('no value');
when too_many_rows then
dbms_output.put_line('too many rows');
end;
輸出結果為:
游標處于關閉狀態
游標指向有效行
---------------
1
游標處于關閉狀態
游標指向有效行
二.動態游標
首先 , 游標變量和游標是兩個不同的概念 . 與游標相似 , 游標變量是指向多行查游標詢的結果集的當前行,游標是靜態的 , 游標變量是動態的 ,. 同時游標變量并不參與與特定的查詢綁定,可以修改查詢的sql語句, 所以可以為任何兼容的查詢打開游標變量 , 從而提高靈活性 . 而且 , 還可以將新的值賦予游標變量 , 將它作為參數傳遞給本地和存儲過程 . 游標變量針對每個 PL/SQL 用戶都是可用的 , 可以在客戶端完全使用游標變量 .ORACLE 服務器同樣包含 PL/SQL 引擎 , 可以將游標變量在應用和服務器之間進行傳遞 .
.游標變量:
聲明游標實際上是創建一個指針 , 指針具有數據類型 REF X.REF 是 REFERENCE ,X 是表示類對象 . 因此 , 游標變量具有數據類型 REF CURSOR.
注 : 游標總是指向相同的查詢工作區 , 游標變量能夠指向不同的工作區 , 因此游標和游標變量不能互操作 .
1定義 REF CURSOR 類型 , 創建游標變量有兩個步驟:
1) 定義 REF CURSOR 類型
語法格式 :
TYPE ref_type_name
IS
REF CURSOR [RETURN return_type]
說明 :
ref_type_name 是游標變量中使用的類型 ;return_type 必須是一個記錄 (record) 或者數據庫表中的一行 .(rowtype)
下面定義 一個 REF CURSOR 類型游標
DELARE
TYPE xs_cur
IS
REF CURSOR RETURN xs%ROWTYPE;
注意 :
REF CURSOR類型既可以是強類型 , 也可以是弱類型 , 區別是強類型有返回類型 , 弱類型沒有. 如下所示
DECLARE
TYPE xs_cur IS REF CURSOR RETURN xs%ROWTYPE;-- 強類型
TYPE mycur IS REF CURSOR;-- 弱類型
2)聲明這種類型的游標變量 : 一旦定義了 REF CURSOR 類型就可以在 PL/SQL 塊或子程序中聲明這個游標變量. 如 :
DECARE
TYPE xs_cur REF CURSOR RETURN xs%ROWTYPE;
xscur xs_cur;
當然 , 在 RETURN 子句中也可以定義自定義的 RECORD 類型 , 如 :
DECLARE
TYPE kc_cj IS RECORD
(
kch number (4),
kcm number(10),
cj number(4,2)
);
TYPE kc_cjcur IS REF CURSOR RETURN kc_cj;
此外 , 也可以聲明游標變量作為函數和過程的參數 . 例如 :
DECLARE
TYPE xs_cur IS REF CURSOR RETURN xs%ROWTYPE;
PROCEDURE open_xs (xscur IN OUT xs_cur)IS
......................
3.控制游標變量
使用游標變量時 , 要遵循以下步驟 :OPEN-FETCH-CLOSE
OPEN 語句與多行查詢的游標變量相關聯 , 它執行查詢 , 標志結果集
語法格式 :
OPEN {cursor_variable|:host_cursor_variable }FOR
{
select_statement|dynamic_string[USING bind_argument[,......]]
}
如 :
IF NOT xscur%ISOPEN THEN
OPEN xscur FOR SELECT * FROM xs;
END IF ;
游標變量同樣可以使用游標屬性 :%FOUND,%ISOPEN,%ROWTYPE
在使用過程中 , 其他的 OPEN 語句可以為不同的查詢打開相同的游標變量 . 因此 , 在重新打開之前 , 不要關閉游標變量 . 可以打開游標 , 并作為參數傳遞給存儲過程 . 如 :
CREATE PACKAGE xs_data AS
...
TYPE xs_cur IS REF CURSOR RETURN xs%ROWTYPE;
RROCEDURE open_xs(xscur IN OUT xs_cur);
END xs_data;
CREATE PACKAGE BODY xs_data AS
...
PROCEDURE open_xs(xscur IN OUT xs_cur)
AS
BEGIN
OPEN xscur FOR SELECT * FROM xs;
END open_xs;
END xs_data;
當聲明一個游標變量作為打開游標變量子程序的參數時 , 必須定義 IN OUT 模式 . 也就是說 , 子程序可以將一個打開的游標變量傳遞給調用者 .
例子1:弱類型游標變量
declare
type RefEmpCur is ref cursor; --聲明引用游標類型 游標返回的類型沒有限制
EmpCur RefEmpCur; --游標變量
EmpRow emp%rowtype; --存儲游標查詢到得結果
flag int:=0;
begin
flag :=&flag;
if flag=0 then
open EmpCur for select * from emp where sal>500 and sal<2000;
elsif flag=1 then
open EmpCur for select * from emp where sal>=2000;
elsif flag=2 then
open EmpCur for select * from dept; --弱類型游標對目標表沒有限制,數據可以使來自任何表
else
open EmpCur for select * from emp;
end if;
/* for循環不能用于Ref游標,因為它是自動打開游標
for EmpRow in EmpCur loop
DBMS_output.put_line('empno='||EmpRow.empno);
end loop;
*/
loop
exit when EmpCur%notfound; --如果沒有查詢到數據就退出
fetch EmpCur into EmpRow;
DBMS_output.put_line('empno='||EmpRow.empno);
end loop;
close EmpCur;
end;
/
例子2:強類型游標變量
declare
type RefEmpCur is ref cursor return emp%rowtype; --游標僅能打開emp表的數據
EmpCur RefEmpCur; --游標變量
EmpRow emp%rowtype; --存儲游標查詢到得結果
flag int:=0;
begin
flag :=&flag;
if flag=0 then
open EmpCur for select * from emp where sal>500 and sal<2000;
elsif flag=1 then
open EmpCur for select * from emp where sal>=2000;
else
open EmpCur for select * from emp;
end if;
loop
exit when EmpCur%notfound; --如果沒有查詢到數據就退出
fetch EmpCur into EmpRow;
DBMS_output.put_line('empno='||EmpRow.empno);
end loop;
close EmpCur;
end;
/
限制
1.不能在程序包中聲明游標變量
2.遠程子程序不能接受游標變量的值
3.不能使用比較操作符對游標變量進行相等或不相等測試
4.不能將空值賦予游標變量
5.表不能存儲游標變量的值
總結
以上是生活随笔為你收集整理的Oracle存储过程+游标的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 消息称微软正开发 Surface Ear
- 下一篇: ASP.NET(99):WebServi