SQLServer遊標詳解

參考資料
http://technet.microsoft.com/zh-cn/library/ms181441(v=sql.105).aspx
一、遊標概念
我們知道,關係數據庫所有的關係運算其實是集合與集合的運算,它的輸入是集合輸出同樣是集合,有時需要對結果集逐行進行處理,這時就需要用到遊標。我們對遊標的使用一本遵循“五步法”:聲明遊標—>打開遊標—>讀取數據—>關閉遊標—>刪除遊標。以下就從這五步對遊標的使用進行說明,並給出具體實例。

二、“五步法”講解
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
Go
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
for
select * 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
複製代碼
三、用到的數據
1、創建表的腳本
複製代碼
CREATE TABLE [dbo].[XSB](
[學號] char NOT NULL,
[姓名] char NOT NULL,
[性別] [bit] NULL,
[出生時間] [date] NULL,
[專業] char NULL,
[總學分] [int] NULL,
[備註] varchar 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
複製代碼
2、插入數據
複製代碼
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
複製代碼

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