T-SQL遊標

  遊標是面向行的,它會使開發人員變懶,懶得去想用面向集合的查詢方式實現某些功能。

  在性能上,遊標會遲更多的內存,減少可用的併發,佔用帶寬,鎖定資源,當然還有更多的代碼量。

  用一個比喻來說明爲什麼遊標會佔用更多的資源。當你從ATM機取款的時候,是一次取1000的效率更高呢,還是10次100呢?

既然遊標那麼多缺點,爲什麼要學習遊標呢?

  1.   現存系統有一些遊標,我們查詢必須通過遊標來實現。

  2.   作用一個備用方式,當使用while、子查詢,臨時表,表變量,自建函數或其他方式仍然無法實現某些查詢的時候,可以使用遊標實現。

  遊標的定義語法:

DECLARE cursor_name CURSOR[ LOCAL | GLOBAL ][ FORWARD_ONLY | SCROLL ][ STATIC | KEYSET | DYNAMIC | FAST_FORWARD ][ READ_ONLY | SCROLL_LOCKS | OPTIMISTIC ][ TYPE_WARNING ]FOR select_statement      [ FOR UPDATE [ OF column_name [ ,...n ] ] ][;]

T-SQL中游標的生命週期以及實現

  1、定義一個遊標

  在T-SQL中,定義一個遊標可以使非常簡單,也可以相對複雜,這主要取決於遊標的參數。而遊標的參數設置取決於你對遊標原理的瞭解程度。

  遊標其實可以理解成一個定義在特定數據集上的指針,我們可以控制這個指針遍歷數據集,或者僅僅是指向特定的行,所以歐標是定義在以SELECT開始的數據集上的。

   遊標分爲遊標類型和遊標變量,對於遊標變量來說,遵循T-SQL變量的定義方法。遊標變量支持兩種方式賦值,定義時賦值和先定義後賦值,定義遊標變量像定義其他局部變量一樣,在遊標前加”@”,注意,如果定義全局的遊標,只支持定義時直接賦值,並且不能在遊標名稱前面加“@”,兩種定義方式如下:

  --定義後直接複製DECLARE test_Cursor CURSORFORSELECT*FROM Person  --先定義後複製DECLARE@TEST_Cursor2CURSORSET@test_Cursor2=CURSORFORSELECT*FROM Person

  參數解釋:

1、LOCAL和GLOBAL二選一

--全局遊標,跨GLOBALDECLARE test_Cursor CURSOR GLOBAL FORSELECT*FROM Person--局部遊標,跨LOCALDECLARE test_Cursor2 CURSOR LOCAL FORSELECT*FROM Person--用GO結束上面的作用域GOOPEN test_CursorOPEN test_Cursor2    --此行代碼報錯,報遊標不存在,因此可以理解局部遊標不跨批處理,批處理結束後,將被隱式釋放,無法在其他批處理中調用

   如果不指定遊標作用域,默認作用域爲GLOBAL。

2、FORWARD_ONLY 和 SCROLL 二選一

   FORWARD_ONLY意味着遊標只能從數據集開始向數據集結束的方向讀取,FETCH NEXT是唯一的選項,而SCROLL支持遊標在定義的數據集中向任何方向,或任何位置移動。

--不加參數,默認爲Forward_OnlyDECLARE test_Cursor CURSORFORSELECT*FROM Person--加Forward_OnlyDECLARE test_Cursor2 CURSOR FORWARD_ONLY FORSELECT*FROM Person--加SCROLLDECLARE test_Cursor3 CURSOR SCROLL FORSELECT*FROM PersonOPEN test_CursorOPEN test_Cursor2OPEN test_Cursor3FETCH LAST FROM test_Cursor            --報錯 fetch: 提取類型 last 不能與只進遊標一起使用。FETCH LAST FROM test_Cursor2          --報錯 fetch: 提取類型 last 不能與只進遊標一起使用。FETCH LAST FROM test_Cursor3       --正確執行

3、STATIC KEYSET DYNAMIC 和 FAST_FORWARD 四選一

  這四個關鍵字是遊標所在數據集所反映的表數據和遊標讀取出數據的關係

  • STATIC:當遊標被建立時,將會創建FOR後面的SELECT語句所包含數據集的副本存入tempdb數據庫中,任何對於底層表內數據的更改不會影響到遊標內容。

  • DYNAMIC:和STATIC完全相反的選項,當底層數據庫更改時,遊標的內容也會隨之得到反映,在下一次fecth中,數據內容會隨之更愛。

  • KEYSET:可以理解爲介於STATIC和DYNAMIC的折中方案,將遊標所在結果集的唯一能確定每一行的主鍵存入tempdb,當結果集中任何行改變或者刪除時,@@FETCH_STATUS會爲-2,KEYSET無法探測新加入的數據。

  • FAST_FORWARD:可以理解爲FORWARD_ONLY的優化版本。FORWARD_ONLY執行的是靜態計劃,而FAST_FORWARD是根據情況進行選擇採用動態計劃還是靜態計劃,大多數情況下FAST_FORWARD要比FORWARD_ONLY性能略好。

4、READ_ONLY SCROLL_LOCKS OPTIMISTIC 三選一

  • READ_ONLY:意味着聲明的遊標只能讀取數據,遊標不能做任何更新操作

  • SCROLL_LOCKS:是另一種極端,將讀入遊標的所有數據進行鎖定,防止其他程序進行更改,以確保更新的絕對成功。

  • OPTIMISTIC:相對比較好的一個選擇,OPTIMISTIC不鎖定任何數據,當需要在遊標中更新數據時,如果底層表數據更新,則遊標內數據更新不成功,如果,底層表數據未更新,則遊標內表數據可以更新。

打開遊標

  當定義完遊標後,遊標需要打開後使用,只需一行代碼便可打開遊標:

  OPEN test_Cursor

  注意,當全局遊標和局部遊標變量重名時,默認會打開局部變量遊標。

使用遊標

  遊標的使用分爲兩部分,一部分是操作遊標在數據集內的指向,另一部分是將遊標所指向的行的部分或全部內容進行操作。

只有支持6中移動宣杭,分別爲第一行(FIRST),最後一行(LAST),下一行(NEXT),上一行(PRIOR),直接跳到某行(ABSOLUTE(n)),相對於目前跳幾行(RELATIVE(n))。

  例如:

  DECLARE test_Cursor CURSOR SCROLL FORSELECT name FROM Person  OPEN test_Cursor  DECLARE@cNVARCHAR(10)  --取下一行FETCHNEXTFROM test_Cursor INTO@cPRINT@c--取最後一行FETCH LAST FROM test_Cursor INTO@cPRINT@c--取第一行FETCH FIRST FROM test_Cursor INTO@cPRINT@c--取上一行FETCH PRIOR FROM test_Cursor INTO@cPRINT@c--取第三行FETCH ABSOLUTE 3FROM test_Cursor INTO@cPRINT@c--取相對目前來說上一行FETCH RELATIVE -1FROM test_Cursor INTO@cPRINT@c

 對於未指定SCROLL選項的遊標來說(未指定是隻進遊標),只支持NEXT取值。

遊標經常會和全局變量@@FETCH_STATUS與WHILE循環來共同使用,以達到遍歷遊標所在數據集的目的。

  例如:

  DECLARE test_Cursor CURSOR SCROLL FORSELECT id,name FROM Person  OPEN test_Cursor  DECLARE@idint  DECLARE@nameNVARCHAR(10)  WHILE@@FETCH_STATUS=0BEGINPRINT@idPRINT@nemFETCHNEXTFROM test_Cursor INTO@id,@nameENDCLOSE test_Cursor  DEALLOCATE test_Cursor

關閉遊標

  在遊標使用完之後,一定要記得關閉,只需要一行代碼:CLOSE+遊標名稱

  CLOSE test_Cursor

釋放遊標

當遊標不再需要被使用後,釋放遊標,只需要一行代碼:DEALLOCATE+遊標名稱

  DEALLOCATE test_Cursor

對於遊標一些優化建議

  • 如果能不用遊標,儘量不要使用遊標

  • 用完用完之後一定要關閉和釋放

  • 儘量不要在大量數據上定義遊標

  • 儘量不要使用遊標上更新數據

  • 儘量不要使用insensitive, static和keyset這些參數定義遊標

  • 如果可以,儘量使用FAST_FORWARD關鍵字定義遊標

  • 如果只對數據進行讀取,當讀取時只用到FETCH NEXT選項,則最好使用FORWARD_ONLY參數

  到生命週期來談遊標。遊標是非常邪惡的一種存在,使用遊標經常會比使用面向集合的方法慢2-3倍,當遊標定義在大數據量時,這個比例還會增加。如果可能,儘量使用while,子查詢,臨時表,函數,表變量等來替代遊標,記住,遊標永遠只是你最後無奈之下的選擇,而不是首選。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章