oracle儲存過程----遊標(cursor)的學習
oracle儲存過程—-遊標(cursor)的學習
今天又學了一個新的概念Cursor
,即遊標。
接上一篇,oracle儲存過程—-儲存過程執行簡單的增刪改查sql ,上一篇中,寫到儲存過程的查詢sql
,當時在寫到查詢的時候,忽然不知道怎麼對查詢結果是多條的資料,如何操作遍歷呢。
遊標(Cursor
)的定義與作用
遊標是SQL的一個記憶體工作區,由系統或使用者以變數的形式定義。遊標的作用就是用於臨時儲存從資料庫中提取的資料塊。在某些情況下,需要把資料從存放在磁碟的表中調到計算機記憶體中進行處理,最後將處理結果顯示出來或最終寫回資料庫。這樣資料處理的速度才會提高,否則頻繁的磁碟資料交換會降低效率。
不過我是新手,我理解的是,遊標就像我們查詢資料返回的集合型別,比如List
遊標(Cursor
)的應用場景
就以我現在理解的來說,遊標既然像查詢結果集list
,那使用cursor
的情況,肯定會有下邊這兩種情況,才可能使用到cursor
:
- 需要依據查詢到的結果集,作為條件,進行下一步的查詢。
- 需要在結果集某個位置上,滿足某種條件時,對資料進行不同的修改。
遊標(Cursor
)的使用考慮
上邊的兩種情況,都會涉及到遍歷資料(儲存過程
裡如何遍歷資料,下一篇再寫),所以遊標的使用也需要注意:
- 比如,你的查詢結果集很大,首先佔了很多的記憶體,其次,遍歷這麼龐大的結果集,效率也肯定高不了。
- 另外,如果還有其他方式解決問題的話,那就不要選擇使用遊標(網上搜來的),我沒測試過效率,但是遊標在使用中,的確有,定義遊標、開啟遊標、關閉遊標,這些操作,這樣也肯定快不了多少。
遊標(Cursor
)的類別
遊標有兩種,顯示遊標
和隱式遊標
。
隱式遊標:
上一篇中用到的SELECT…INTO…查詢語句,一次只能從資料庫中提取一行資料,對於這種形式的查詢和DML操作,系統都會使用一個隱式遊標。
DML操作和單行SELECT語句會使用隱式遊標,它們是:
- 插入操作:INSERT。
- 更新操作:UPDATE。
- 刪除操作:DELETE。
- 單行查詢操作:SELECT … INTO …。
(2018-08-19補充)當然,隱式遊標還有一種寫法,如下,並沒有定義遊標:
create or replace procedure test_cryptic_procedure
as
begin
for cryptic_rec in (
select * from ly_ds
)
loop
dbms_output.put_line('名稱:'||cryptic_rec.ly_mc||' 年齡:'||cryptic_rec.ly_nl);
end loop;
end;
如下方式執行:
set serveroutput on;
begin
test_cryptic_procedure;
end;
將會看到如下的結果:
匿名塊已完成
名稱:eee 年齡:23
名稱:王五 年齡:22
名稱:趙六六 年齡:29
名稱:李四 年齡:20
提示一下:因為隱式遊標預設開啟遊標,但是如果呼叫%ISOPEN
卻返回false
。這個注意一下,如果還沒懂%ISOPEN
啥意思,那暫且不要管這個提示了。
顯式遊標:
顯示遊標還分為靜態遊標
和動態遊標
。顯示遊標的寫法會在儲存過程中定義Cursor
,並且一般都有固定的四個步驟:宣告遊標
、開啟遊標
、提取資料
、關閉遊標
。顯式遊標開啟後,必須顯式地關閉。遊標一旦關閉,遊標佔用的資源就被釋放,遊標變成無效,必須重新開啟才能使用。
隱式遊標其實預設已經開啟遊標,並在執行完成後關閉遊標釋放資源。
&ems;如果你想詳細瞭解一下遊標的概念
,我是參考了下邊這兩篇文章:
遊標(Cursor
)的例子
下邊是我學習後寫的第一個遊標的例子。不像上邊那個隱式遊標的例子連遊標都沒有定義,下邊這個是定義了遊標,但是沒有去開啟,for
迴圈會預設開啟遊標的。
create or replace procedure test_select3_procedure
(sex varchar)
AS
--遊標的定義
Cursor test_cursor is
select id,ly_mc,LY_NB,ly_nl from ly_ds where LY_NB=sex;
cur test_cursor%rowtype; --遊標的型別,我理解類似於list的泛型
BEGIN
for cur in test_cursor loop
exit when test_cursor%notfound;
dbms_output.put_line('資料是:'||cur.id||'_'||cur.ly_mc||'_'||cur.LY_NB||'_'||cur.ly_nl);
end loop;
END;
上邊,首先看Cursor test_cursor is
這一行,它的意思是定義一個遊標,test_cursor
為你要定義的名字,而is
後邊是一個sql,也就是說當前這個sql的查詢結果,賦值給遊標test_cursor
。
然後,往下,接著cur test_cursor%rowtype
,這個是定義了一個型別,而這個型別,即是遊標test_cursor
的返回結果型別,型別的名字為cur
。有點類似於java語言
中List
集合中的一個泛型
。
另外,關於for
是一個迴圈的寫法,for cur in test_cursor
,即,從遊標test_cursor
中取出一個結果cur
。
還有,注意,loop
和end loop
這是一個迴圈的開始標誌和結束標誌,但它倆兄弟是一個很執著的迴圈,如果沒有定義退出條件,永遠不會退出的,所以在上邊的迴圈裡邊,有了退出條件exit when test_cursor%notfound;
,即當遊標test_cursor
中沒有資料了,就退出迴圈。
當然loop
迴圈的退出,發生下邊的情況,才能退出:
- 有exit,並滿足條件後退出。
- loop中丟擲了異常。
- 存在
goto
標識。
最後,可能大家注意到,上邊並沒有顯示遊標中一定有的四個步驟,
宣告遊標
、開啟遊標
、提取資料
、關閉遊標
,那是因為for
迴圈這樣的遍歷會自動給我們開啟遊標,並在結束後關閉遊標,下邊會學習其他的遍歷方法,但是都沒有for
這樣智慧,所以覺得for
循環遊標,是個很好的選擇。
執行上邊的儲存過程:
set serveroutput on;
execute test_select3_procedure('女');
會得到如下的遍歷結果,正是我根據條件女
查詢到的結果集。
PROCEDURE TEST_SELECT3_PROCEDURE 已編譯
匿名塊已完成
資料是:2_王五_女_22
資料是:3_趙六六_女_29
資料是:4_李四_女_20