遊標是面向行的,它會使開發人員變懶,懶得去想用面向集合的查詢方式實現某些功能。數據庫
在性能上,遊標會遲更多的內存,減小可用的併發,佔用帶寬,鎖定資源,固然還有更多的代碼量。併發
用一個比喻來講明爲何遊標會佔用更多的資源。當你從ATM機取款的時候,是一次取1000的效率更高呢,仍是10次100呢?ide
既然遊標那麼多缺點,爲何要學習遊標呢?函數
現存系統有一些遊標,咱們查詢必須經過遊標來實現。性能
做用一個備用方式,當使用while、子查詢,臨時表,表變量,自建函數或其餘方式仍然沒法實現某些查詢的時候,可使用遊標實現。學習
遊標的定義語法:fetch
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中游標的生命週期以及實現優化
一、定義一個遊標spa
在T-SQL中,定義一個遊標可使很是簡單,也能夠相對複雜,這主要取決於遊標的參數。而遊標的參數設置取決於你對遊標原理的瞭解程度。
遊標其實能夠理解成一個定義在特定數據集上的指針,咱們能夠控制這個指針遍歷數據集,或者僅僅是指向特定的行,因此歐標是定義在以SELECT開始的數據集上的。
遊標分爲遊標類型和遊標變量,對於遊標變量來講,遵循T-SQL變量的定義方法。遊標變量支持兩種方式賦值,定義時賦值和先定義後賦值,定義遊標變量像定義其餘局部變量同樣,在遊標前加」@」,注意,若是定義全局的遊標,只支持定義時直接賦值,而且不能在遊標名稱前面加「@」,兩種定義方式以下:
--定義後直接複製DECLARE test_Cursor CURSORFORSELECT*FROM Person --先定義後複製DECLARE@TEST_Cursor2CURSORSET@test_Cursor2=CURSORFORSELECT*FROM Person
參數解釋:
一、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。
二、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 --正確執行
三、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性能略好。
四、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,子查詢,臨時表,函數,表變量等來替代遊標,記住,遊標永遠只是你最後無奈之下的選擇,而不是首選。