SQLServer 遊標詳解
一、用到的數據
CREATE TABLE [dbo].[XSB]( [學號] [char](6) NOT NULL, [姓名] [char](8) NOT NULL, [性別] [bit] NULL, [出生時間] [date] NULL, [專業] [char](12) NULL, [總學分] [int] NULL, [備註] [varchar](500) NULL, PRIMARY KEY CLUSTERED ( [學號] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE =OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] GO insert into XSB values (‘081101‘,‘王林‘,‘true‘,‘1990-2-10‘,‘計算機‘,50,null), (‘081102‘,‘陳平‘,‘true‘,‘1991-2-1‘,‘計算機‘,50,null), (‘081103‘,‘王燕‘,‘false‘,‘1989-10-6‘,‘計算機‘,50,null), (‘081104‘,‘韋嚴平‘,‘true‘,‘1990-8-26‘,‘計算機‘,50,null), (‘081106‘,‘李芳芳‘,‘true‘,‘1990-11-20‘,‘計算機‘,50,null), (‘081107‘,‘李明‘,‘true‘,‘1990-5-1‘,‘計算機‘,54,‘已提前修完數據結構,並獲學分‘), (‘081108‘,‘林一凡‘,‘true‘,‘1989-8-5‘,‘計算機‘,52,‘已提前修完一門課‘), (‘081109‘,‘張強民‘,‘true‘,‘1989-8-11‘,‘計算機‘,50,null), (‘081110‘,‘張蔚‘,‘false‘,‘1991-7-22‘,‘計算機‘,50,‘三好生‘), (‘081111‘,‘趙琳‘,‘false‘,‘1989-10-6‘,‘計算機‘,50,null), (‘081113‘,‘嚴紅‘,‘false‘,‘1989-8-11‘,‘計算機‘,48,‘一門不及格,待補考‘), (‘081201‘,‘王敏‘,‘true‘,‘1989-6-10‘,‘通信工程‘,42,null), (‘081202‘,‘王林‘,‘true‘,‘1989-6-10‘,‘通信工程‘,40,‘一門不及格,待補考‘) GO
二、遊標概念
我們知道,關系數據庫所有的關系運算其實是集合與集合的運算,它的輸入是集合輸出同樣是集合,有時需要對結果集逐行進行處理,這時就需要用到遊標。我們對遊標的使用一本遵循“五步法”:聲明遊標—>打開遊標—>讀取數據—>關閉遊標—>刪除遊標。以下就從這五步對遊標的使用進行說明,並給出具體實例。
三、“五步法”講解
1、聲明遊標(DECLARE CURSOR)
(1) DECLARE CURSOR 既接受基於 ISO 標準的語法,也接受使用一組 Transact-SQL 擴展的語法。
--ISO 語法 DECLARE cursor_name [ INSENSITIVE ] [ SCROLL ] CURSOR FOR select_statement [ FOR { READ ONLY | UPDATE [ OF column_name [ ,...n ] ] } ] [;] --Transact-SQL 擴展語法 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擴展語法”進行介紹,主要遊標介紹作用域、方向及類型
(2)遊標的作用域
LOCAL
說明所聲明的遊標為局部的,其作用域為創建它的批處理、存儲過程或觸發器,即在批處理、調用它的存儲過程或觸發器執行完成後,該遊標被系統隱式釋放。但,若遊標作為存儲過程OUTPUT 的輸出參數,在存儲過程終止後給遊標變量分配參數可以繼續引用遊標,如果 OUTPUT 參數將遊標傳遞回來,則遊標在最後引用它的變量釋放或離開作用域時釋放。
GLOBAL
指定該遊標的作用域對來說連接是全局的。在由連接執行的任何存儲過程或批處理中,都可以引用該遊標名稱。該遊標僅在斷開連接時隱式釋放。
註意:如果 GLOBAL 和 LOCAL 參數都未指定,則默認值由 default to local cursor 數據庫選項的設置控制。在 SQL Server 7.0 版中,該選項默認為 FALSE,以便與 SQL Server 的早期版本匹配,在早期版本中,所有遊標都是全局的。
declare testcur cursor --聲明時未指定"local"或"global"關鍵字,系統默認遊標是"global(全局)"的. for select 學號,姓名 from XSB GO open testcur fetch next from testcur GO -------------------------------------------- declare testcur cursor local --聲明時指定"local"關鍵字 for select 學號,姓名 from XSB open testcur fetch next from testcur GO --在這個批處理結束後,遊標自動釋放,即失效 ----------------------------------------------- declare testcur1 cursor local --聲明時指定"local"關鍵字 for select 學號,姓名 from XSB GO --在這個批處理後,遊標就失效了,後面對遊標操作時,提示"名為 ‘testcur1‘ 的遊標不存在"錯誤. open testcur1 fetch next from testcur1 GO
(3)遊標方向
FORWARD_ONLY
指定遊標只能從第一行滾動到最後一行。FETCH NEXT 是唯一支持的提取選項。如果在指定 FORWARD_ONLY 時不指定 STATIC、KEYSET 和 DYNAMIC 關鍵字,則遊標作為 DYNAMIC 遊標進行操作。如果 FORWARD_ONLY 和 SCROLL 均未指定,則除非指定 STATIC、KEYSET 或 DYNAMIC 關鍵字,否則默認為 FORWARD_ONLY。STATIC、KEYSET 和 DYNAMIC 遊標默認為 SCROLL。與 ODBC 和 ADO 這類數據庫 API 不同,STATIC、KEYSET 和 DYNAMIC Transact-SQL 遊標支持 FORWARD_ONLY。
SCROLL
指定所有的提取選項(FIRST、LAST、PRIOR、NEXT、RELATIVE、ABSOLUTE)均可用。如果未在 ISO DECLARE CURSOR 中指定 SCROLL,則 NEXT 是唯一支持的提取選項。如果也指定了 FAST_FORWARD,則不能指定 SCROLL。
declare directionCur cursor --不指定移動方向,則默認為“forward_only” for select 學號,姓名 from XSB Go open directionCur fetch next from directionCur Go fetch prior from directionCur --錯誤“提取類型 prior 不能與只進遊標一起使用。” Go close directionCur2 deallocate directionCur2 Go ----------------------------------------- declare directionCur1 cursor forward_only --指定“forward_only”方向 for select 學號,姓名 from XSB Go open directionCur1 fetch next from directionCur1 Go fetch prior from directionCur1 --錯誤“提取類型 prior 不能與只進遊標一起使用。” Go close directionCur2 deallocate directionCur2 Go ----------------------------------------- declare directionCur2 cursor scroll --指定“scroll”方向 for select 學號,姓名 from XSB Go open directionCur2 fetch next from directionCur2 Go fetch prior from directionCur2 Go close directionCur2 deallocate directionCur2 Go --------------------------------- --結論:若遊標沒有指定任何訪問或類型參數,則默認為全局、只進、動態遊標。(這是在SQLServer2008 R2上的測試結果,具體取決於軟件設置) --"scroll"和"fast_forward"不能一起使用 declare forwardTest cursor scroll fast_forward --報錯“遊標選項 SCROLL 和 FAST_FORWARD 沖突。” for select 學號,姓名 from XSB Go --在 SQL Server 2000 中,FAST_FORWARD 和 FORWARD_ONLY 遊標選項是互相排斥的。如果指定了二者,則會引發錯誤。 --在 SQL Server 2005 及更高版本中,這兩個關鍵字可以用在同一個 DECLARE CURSOR 語句中。 declare forwardTest1 cursor forward_only fast_forward for select * from XSB Go
(4)遊標類型
STATIC
定義一個遊標,以創建將由該遊標使用的數據的臨時復本。對遊標的所有請求都從 tempdb 中的這一臨時表中得到應答;因此,在對該遊標進行提取操作時返回的數據中不反映對基表所做的修改,並且該遊標不允許修改。及該遊標是只讀的。
KEYSET
指定當遊標打開時,遊標中行的成員身份和順序已經固定。對行進行唯一標識的鍵集內置在 tempdb 內一個稱為 keyset 的表中。
DYNAMIC
定義一個遊標,以反映在滾動遊標時對結果集內的各行所做的所有數據更改。行的數據值、順序和成員身份在每次提取時都會更改。
FAST_FORWARD
指定啟用了性能優化的 FORWARD_ONLY、READ_ONLY 遊標。如果指定了 SCROLL 或 FOR_UPDATE,則不能也指定 FAST_FORWARD。
2、打開遊標(OPEN)
語法
OPEN { { [ GLOBAL ] cursor_name } | cursor_variable_name }
示例
--打開遊標並讀取所有行 declare XSBcur cursor --聲明 for Select * from XSB open XSBcur --打開 fetch next from XSBcur --獲取數據 while @@FETCH_STATUS = 0 begin fetch next from XSBcur end close XSBcur --關閉 deallocate XSBcur --刪除
全局變量 @@CURSOR_ROWS
該變量保存著最後打開的遊標中的數據行數,當其值為0時,表示沒有遊標打開;其值為-1時,表示遊標為動態的;當其值為-m(m為正整數)時,遊標采用異步方式填充,m為當前鍵集中已填充的行數;當其值為m(m為正整數)時,遊標已被完全填充,m是遊標中的數據行數。
3、讀取數據
語法
FETCH [ [ NEXT | PRIOR | FIRST | LAST | ABSOLUTE { n | @nvar } | RELATIVE { n | @nvar } ] FROM ] { { [ GLOBAL ] cursor_name } | @cursor_variable_name } [ INTO @variable_name [ ,...n ] ] --into說明將讀取的遊標數據存放到指定的變量中
示例
declare stuCur cursor scroll for select 學號,姓名 from XSB GO open stuCur Go --讀取數據開始 fetch next from stuCur --讀取當前行的下一行,並使其置為當前行(剛開始時遊標置於表頭的前一行,即若表是從0開始的,遊標最初置於-1處,所以第一次讀取的是頭一行) fetch prior from stuCur --讀取當前行的前一行,並使其置為當前行 fetch first from stuCur --讀取遊標的第一行,並使其置為當前行(不能用於只進遊標) fetch last from stuCur --讀取遊標的最後一行,並使其置為當前行(不能用於只進遊標) fetch absolute 2 from stuCur --讀取從遊標頭開始向後的第2行,並將讀取的行作為新的行 fetch relative 3 from stuCur --讀取從當前行開始向後的第3行,並將讀取的行作為新的行 fetch relative-2 from stuCur --讀取當前行的上兩行,並將讀取的行作為新的行 --讀取數據結束 GO close stuCur Go deallocate stuCur Go
全局變量 @@FETCH_STATUS
FETCH語句的執行狀態保存在全局變量@@FETCH_STATUS中,其值為0表示上一個FETCH執行成功;為-1表示所要讀取的行不在結果集中;為-2表示被提取的行已不存在(已被刪除)。
4、關閉遊標(CLOSE)
語法
CLOSE { { [ GLOBAL ] cursor_name } | cursor_variable_name }
示例
close stuCur --若該遊標事先聲明並已打開 Go
5、刪除遊標(DEALLOCATE)
對遊標進行操作的語句使用遊標名稱或遊標變量引用遊標。DEALLOCATE 刪除遊標與遊標名稱或遊標變量之間的關聯。如果一個名稱或變量是最後引用遊標的名稱或變量,則將釋放遊標,遊標使用的任何資源也隨之釋放。用於保護提取隔離的滾動鎖在 DEALLOCATE 上釋放。用於保護更新(包括通過遊標進行的定位更新)的事務鎖一直到事務結束才釋放。
語法
DEALLOCATE { { [ GLOBAL ] cursor_name } | @cursor_variable_name }
示例
遊標變量使用下列兩種方法之一與遊標關聯:
--聲明一個遊標 declare abc cursor scroll forselect * from XSB --(1)通過名稱,使用set語句將遊標設置為遊標變量 declare @mycur cursor set @mycur = abc --(2)也可以不定義遊標名稱而創建遊標並將其與變量關聯 declare @mycursor cursor set @mycursor = cursor local scroll for Select * from XSB
DEALLOCATE @cursor_variable_name 語句只刪除對遊標名稱變量的引用。直到批處理、存儲過程或觸發器結束時變量離開作用域,才釋放變量。在 DEALLOCATE @cursor_variable_name 語句之後,可以使用 SET 語句將變量與另一個遊標關聯。遊標可以理解為指針。
declare @mycur cursor set @mycur = cursor local scroll for select * from XSB deallocate @mycur set @mycur = cursor local scroll for select * from XSB GO --不必顯式釋放遊標變量。變量在離開作用域時被隱式釋放。
以下腳本顯示遊標如何持續到最後的名稱或持續到引用它們的變量已釋放。
USE AdventureWorks2008R2; GO -- Create and open a global named cursor that -- is visible outside the batch. DECLARE abc CURSOR GLOBAL SCROLL FOR SELECT * FROM Sales.SalesPerson; OPEN abc; GO -- Reference the named cursor with a cursor variable. DECLARE @MyCrsrRef1 CURSOR; SET @MyCrsrRef1 = abc; -- Now deallocate the cursor reference. DEALLOCATE @MyCrsrRef1; -- Cursor abc still exists. FETCH NEXT FROM abc; GO -- Reference the named cursor again. DECLARE @MyCrsrRef2 CURSOR; SET @MyCrsrRef2 = abc; -- Now deallocate cursor name abc. DEALLOCATE abc; -- Cursor still exists, referenced by @MyCrsrRef2. FETCH NEXT FROM @MyCrsrRef2; -- Cursor finally is deallocated when last referencing -- variable goes out of scope at the end of the batch. GO -- Create an unnamed cursor. DECLARE @MyCursor CURSOR; SET @MyCursor = CURSOR LOCAL SCROLL FOR SELECT * FROM Sales.SalesTerritory; -- The following statement deallocates the cursor -- because no other variables reference it. DEALLOCATE @MyCursor; GO
SQLServer 遊標詳解