游標(Cursor)是SQL Server的一種數據訪問機制,它使得程序可以逐行處理數據,即允許用戶訪問單獨的數據行,對每一行數據進行單獨的處理。
一,創建游標對象
創建游標對象,注意不是游標變量,游標名稱不需要帶前導@:
DECLARE cursor_name CURSOR
[ LOCAL | GLOBAL ] [ FORWARD_ONLY | SCROLL ] [ STATIC | KEYSET | DYNAMIC | FAST_FORWARD ] [ READ_ONLY | SCROLL_LOCKS | OPTIMISTIC ] FOR select_statement [ FOR UPDATE [ OF column_name [ ,...n ] ] ] [;]
在創建游標時,需要配置游標的選項,從定義游標的語法中,可以看出,游標共有5個選項。
1,游標的作用域
LOCAL 和 GLOBAL用於定義游標對象的作用域,
- LOCAL指定游標的作用域是本地,默認值是LOCAL。
- GLOBAL指定游標的作用域是全局的。
GLOBAL游標的作用域是連接,由連接內的batch或存儲過程中引用,GLOBAL 游標僅在連接斷開時被釋放。
2,游標滾動的方向
FORWARD_ONLY 和 SCROLL用於指定游標滾動的方向:
- FORWARD_ONLY 指定游標只能向前移動,即只能從第一行滾動到最后一行。FETCH NEXT 語句是唯一受支持的提取方法。
- SCROLL 指定游標可以向前或向后滾動,
默認情況下,游標是FORWARD_ONLY(僅向前游標),只向前滾動。注意,向前一行是指下一行,向后一行是指上一行。當設置游標為FORWARD_ONLY和DYNAMIC時,游標在處理當前行時會檢測到所有更改,這意味着對數據提交的任何修改操作(INSERT,UPDATE和DELETE)都會影響到獲取的數據集,進而影響到FETCH NEXT語句。由於游標無法向后滾動,在某一行之后,上N行的數據修改無法查看,但是可以通過fetch查看下N行的數據修改。
3,游標是否對數據的修改可見
STATIC 指定靜態游標,靜態游標始終是只讀的,在第一次打開游標時,在tempdb上創建游標的完整結果集,從臨時表來響應游標的查詢操作,因此對基表的修改操作,不會影響游標的結果集。在游標關閉之前,游標的結果集保持不變。
DYNAMIC 指定靜態游標,動態游標反映結果集中的所有修改操作。結果集中的行數據值、順序和成員在每次提取時都會改變。所有用戶做的全部UPDATE、INSERT和DELETE語句均通過游標可見。
FAST_FORWARD 指定啟動了性能優化的FORWARD_ONLY、READ_ONLY游標。如果指定了SCROLL或FOR_UPDATE,則不能指定FAST_FORWARD。
KEYSET 指定游標按照鍵集來滾動,游標的結果集(result set)包含兩部分:鍵+其他列,鍵用於唯一標識一行數據,這意味着鍵(key)是由唯一標識數據行的一列或多列構成的,鍵集(KyeSet)是指由一組鍵構成的集合。
在打開游標時,SQL Server在tempdb中創建臨時表keyset,該表是只讀的,游標的滾動是通過keyset來迭代的。因此,當游標打開時,行的順序已經固定。在游標滾動時,按照鍵集對游標的結果集進行join,返回完整的一行數據。
獲取由鍵集驅動的游標時:
- 當一個數據行被刪除時,其key仍然存在於臨時表keyset中,但是不再存在於結果集(result set)中。對該key進行fetch操作,@@FETCH_STATUS返回 -2
- 當插入一個新的數據行時,如果插入操作發生在游標作用域之外,那么插入的數據對當前游標不可見;如果插入操作發生在游標作用域之內,那么插入的數據存在於結果集的末尾。
- 更新鍵值的操作,等價於先刪除舊的數據行,再插入一個新的數據行。
4,游標是否只讀,是否支持定位修改
READ_ONLY 指定游標是只讀的,不能通過游標修改數據。
SCROLL_LOCKS 指定通過游標進行的定位更新或刪除一定會成功,當把行讀入游標時,SQL Server將鎖定這些行,以確保隨后可對它們進行修改,如果還指定了FAST_FORWARD或STATIC,則不能指定SCROLL_LOCKS。
OPTIMISTIC 指定當行被讀入游標之后,如果數據行被修改了,那么通過游標進行的定位更新或定位刪除不成功。
當將行讀入游標后,SQL Server不鎖定行,SQL Server 通過以下兩種方式來檢測數據行是否被修改:
- SQL Server使用表的timestamp列值的比較結果來確定行在讀入游標后是否發生了變化。
- 如果基表不包含timestamp列,SQL Server使用列值得校驗和來檢測當前行得數據值是否被修改。
如果檢測到該行被修改,那么嘗試進行的定位更新或刪除將失敗。如果還指定了FAST_FORWARD,則不能指定OPTIMISTIC。
5,是否通過游標更新數據
FOR UPDATE [OF column_name [,...n]] 用於在游標中定義可以更新的列,如果提供了 [OF column_name [,...n]] 選項,那么僅允許修改列出的列。如果僅指定了FOR UPDATE子句,那么可以修改所有的列。
二,獲取數據和獲取的狀態
通過FETCH命令來獲取游標中的結果集,並把獲取的一行數據的各列賦值給變量列表:
FETCH [ NEXT | PRIOR | FIRST | LAST | ABSOLUTE { n | @nvar } | RELATIVE { n | @nvar } ] FROM { { [ GLOBAL ] cursor_name } | @cursor_variable_name } [ INTO @variable_name [ ,...n ] ]
在賦值時,變量列表 一 一 對應SELECT 子句的字段列表,FETCH 子句可以通過以下選項來設置獲取一行數據的方向:
- NEXT:表示獲取下一行,
- PRIOR:表示獲取上一行,
- FIRST:表示獲取第一行,
- LAST:表示獲取最后一行
- ABSOLUTE n:表示獲取從第一行開始的第n行,n表示第n行的絕對位置,
- RELATIVE n:表示獲取從當前行開始的第n行,n表示第n行的相對位置,
獲取數據行時,是否獲取成功,可以通過全局變量@@FETCH_STATUS來檢測,注意,全局變量的作用域是當前的連接:
@@FETCH_STATUS
該全局變量返回當前連接中打開的任何游標發出的最后一個FETCH語句的狀態,狀態值是整數:
注意,由於@@FETCH_STATUS 用於檢測當前連接上的所有游標對象的狀態,在使用時,要謹慎。在執行FETCH語句后,必須立即對@@FETCH_STATUS進行測試,然后再對其他游標執行任何其他FETCH語句。
三,舉個例子
下面的腳本,定義了一個靜態的只讀游標,演示了游標打開、遍歷和關閉的全過程:
declare @v1 int declare @v2 int declare cursor_name cursor forward_only static read_only for select c1,c2 from table_name; open cursor_name fetch next from cursor_name into @v1, @v2 while @@fetch_status=0 begin -- do something fetch next from cursor_name into @v1, @v2 end close cursor_name deallocate cursor_name
參考文檔: