遊標
遊標(cursor)是系統爲用戶開設的一個數據緩衝區,存放SQL語句的執行結果。每一個遊標區都有一個名字,用戶能夠用SQL語句逐一從遊標中獲取記錄,並賦給主變量,交由主語言進一步處理。html
遊標是處理結果集的一種機制吧,它能夠定位到結果集中的某一行,多數據進行讀寫,也能夠移動遊標定位到你所須要的行中進行操做數據。通常複雜的存儲過程,都會有遊標的出現,他的用處主要有:sql
- 定位到結果集中的某一行。
- 對當前位置的數據進行讀寫。
- 能夠對結果集中的數據單獨操做,而不是整行執行相同的操做。
- 是面向集合的數據庫管理系統和麪向行的程序設計之間的橋樑。
優勢
在數據庫中,遊標是一個十分重要的概念。遊標提供了一種對從表中檢索出的數據進行操做的靈活手段,就本質而言,遊標其實是一種能從包括多條數據記錄的結果集中每次提取一條記錄的機制。遊標老是與一條SQL 查詢語句相關聯由於遊標由結果集(能夠是零條、一條或由相關的選擇語句檢索出的多條記錄)和結果集中指向特定記錄的遊標位置組成。當決定對結果集進行處理時,必須聲明一個指向該結果集的遊標。若是曾經用C 語言寫過對文件進行處理的程序,那麼遊標就像您打開文件所獲得的文件句柄同樣,只要文件打開成功,該文件句柄就可表明該文件。對於遊標而言,其道理是相同的。可見遊標可以實現按與傳統程序讀取平面文件相似的方式處理來自基礎表的結果集,從而把表中數據以平面文件的形式呈現給程序。咱們知道關係數據庫管理系統實質是面向集合的,在MS SQL SERVER 中並無一種描述表中單一記錄的表達形式,除非使用where 子句來限制只有一條記錄被選中。所以咱們必須藉助於遊標來進行面向單條記錄的數據處理。因而可知,遊標容許應用程序對查詢語句select 返回的行結果集中每一行進行相同或不一樣的操做,而不是一次對整個結果集進行同一種操做;它還提供對基於遊標位置而對錶中數據進行刪除或更新的能力;並且,正是遊標把做爲面向集合的數據庫管理系統和麪向行的程序設計二者聯繫起來,使兩個數據處理方式可以進行溝通。數據庫
缺點
遊標速度較慢。緩存
種類
MS SQL SERVER 支持三種類型的遊標:Transact_SQL 遊標,API服務器遊標和客戶遊標。
(1)Transact_SQL 遊標
Transact_SQL 遊標是由DECLARE CURSOR 語法定義、主要用在Transact_SQL腳本、存儲過程和觸發器中。Transact_SQL 遊標主要用在服務器上,由從客戶端發送給服務器的Transact_SQL 語句或是批處理、存儲過程、觸發器中的Transact_SQL 進行管理。 Transact_SQL 遊標不支持提取數據塊或多行數據。
(2)API遊標
API 遊標支持在OLE DB, ODBC 以及DB_library 中使用遊標函數,主要用在服務器上。每一次客戶端應用程序調用API 遊標函數,MS SQL SEVER 的OLE DB 提供者、ODBC驅動器或DB_library 的動態連接庫(DLL) 都會將這些客戶請求傳送給服務器以對API遊標進行處理。
(3)客戶遊標
客戶遊標主要是當在客戶機上緩存結果集時才使用。在客戶遊標中,有一個缺省的結果集被用來在客戶機上緩存整個結果集。客戶遊標僅支持靜態遊標而非動態遊標。因爲服務器遊標並不支持全部的Transact-SQL語句或批處理,因此客戶遊標經常僅被用做服務器遊標的輔助。由於在通常狀況下,服務器遊標能支持絕大多數的遊標操做。因爲API 遊標和Transact-SQL 遊標使用在服務器端,因此被稱爲服務器遊標,也被稱爲後臺遊標,而客戶端遊標被稱爲前臺遊標。在本章中咱們主要講述服務器(後臺)遊標。安全
遊標的分類
根據遊標檢測結果集變化的能力和消耗資源的狀況不一樣,SQL Server支持的API服務器遊標分爲一下4種:性能優化
-
靜態遊標 : 靜態遊標的結果集,在遊標打開的時候創建在TempDB中,不論你在操做遊標的時候,如何操做數據庫,遊標中的數據集都不會變。例如你在遊標打開的時候,對遊標查詢的數據表數據進行增刪改,操做以後,靜態遊標中select的數據依舊顯示的爲沒有操做以前的數據。若是想與操做以後的數據一致,則從新關閉打開遊標便可。服務器
- 動態遊標 : 這個則與靜態遊標相對,滾動遊標時,動態遊標反應結果集中的全部更改。結果集中的行數據值、順序和成員在每次提取時都會變化。全部用戶作的增刪改語句經過遊標都可見。若是使用API函數或T-SQL Where Current of子句經過遊標進行更新,他們將當即可見。在遊標外部所作的更新直到提交時纔可見。
- 只進遊標:只進遊標不支持滾動,只支持從頭至尾順序提取數據,數據庫執行增刪改,在提取時是可見的,但因爲該遊標只能進不能向後滾動,因此在行提取後對行作增刪改是不可見的。
- 鍵集驅動遊標:打開鍵集驅動遊標時,該有表中的各個成員身份和順序是固定的。打開遊標時,結果集這些行數據被一組惟一標識符標識,被標識的列作刪改時,用戶滾動遊標是可見的,若是沒被標識的列增該,則不可見,好比insert一條數據,是不可見的,若可見,須關閉從新打開遊標。 靜態遊標在滾動時檢測不到表數據變化,但消耗的資源相對不多。動態遊標在滾動時能檢測到全部表數據變化,但消耗的資源卻較多。鍵集驅動遊標則處於他們中間,因此根據需求創建適合本身的遊標,避免資源浪費。
遊標的生命週期
遊標的生命週期包含有五個階段:聲明遊標、打開遊標、讀取遊標數據、關閉遊標、釋放遊標。函數
1,聲明遊標
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 ] ] ]
參數說明:sqlserver
- cursor_name:遊標名稱。
- Local:做用域爲局部,只在定義它的批處理,存儲過程或觸發器中有效。
- Global:做用域爲全局,由鏈接執行的任何存儲過程或批處理中,均可以引用該遊標。
- [Local | Global]:默認爲local。
- Forward_Only:指定遊標智能從第一行滾到最後一行。Fetch Next是惟一支持的提取選項。若是在指定Forward_Only是不指定Static、KeySet、Dynamic關鍵字,默認爲Dynamic遊標。若是Forward_Only和Scroll沒有指定,Static、KeySet、Dynamic遊標默認爲Scroll,Fast_Forward默認爲Forward_Only
- Static:靜態遊標
- KeySet:鍵集遊標
- Dynamic:動態遊標,不支持Absolute提取選項
- Fast_Forward:指定啓用了性能優化的Forward_Only、Read_Only遊標。若是指定啦Scroll或For_Update,就不能指定他啦。
- Read_Only:不能經過遊標對數據進行刪改。
- Scroll_Locks:將行讀入遊標是,鎖定這些行,確保刪除或更新必定會成功。若是指定啦Fast_Forward或Static,就不能指定他啦。
- Optimistic:指定若是行自讀入遊標以來已獲得更新,則經過遊標進行的定位更新或定位刪除不成功。當將行讀入遊標時,sqlserver不鎖定行,它改用timestamp列值的比較結果來肯定行讀入遊標後是否發生了修改,若是表不行timestamp列,它改用校驗和值進行肯定。若是已修改改行,則嘗試進行的定位更新或刪除將失敗。若是指定啦Fast_Forward,則不能指定他。
- Type_Warning:指定將遊標從所請求的類型隱式轉換爲另外一種類型時向客戶端發送警告信息。
- For Update[of column_name ,....] :定義遊標中可更新的列。
2,聲明一個動態遊標
declare orderNum_02_cursor cursor scroll for select OrderId from bigorder where orderNum='ZEORD003402'
3,打開遊標
--打開遊標語法 open [ Global ] cursor_name | cursor_variable_name
cursor_name:遊標名,cursor_variable_name:遊標變量名稱,該變量引用了一個遊標。post
--打開遊標 open orderNum_02_cursor
4,提取數據
--提取遊標語法 Fetch [ [Next|prior|Frist|Last|Absoute n|Relative n ] from ] [Global] cursor_name [into @variable_name[,....]]
參數說明:
- Frist:結果集的第一行
- Prior:當前位置的上一行
- Next:當前位置的下一行
- Last:最後一行
- Absoute n:從遊標的第一行開始數,第n行。
- Relative n:從當前位置數,第n行。
- Into @variable_name[,...] : 將提取到的數據存放到變量variable_name中。
例子:
--提取數據 fetch first from orderNum_02_cursor fetch relative 3 from orderNum_02_cursor fetch next from orderNum_02_cursor fetch absolute 4 from orderNum_02_cursor fetch next from orderNum_02_cursor fetch last from orderNum_02_cursor fetch prior from orderNum_02_cursor select * from bigorder where orderNum='ZEORD003402'
例子:
--提取數據賦值給變量 declare @OrderId int fetch absolute 3 from orderNum_02_cursor into @OrderId select @OrderId as id select * from bigorder where orderNum='ZEORD003402'
經過檢測全局變量@@Fetch_Status的值,得到提取狀態信息,該狀態用於判斷Fetch語句返回數據的有效性。當執行一條Fetch語句以後,@@Fetch_Status可能出現3種值:
- 0,Fetch語句成功。
- -1:Fetch語句失敗或行不在結果集中。
- -2:提取的行不存在。
這個狀態值能夠幫你判斷提取數據的成功與否。
declare @OrderId int fetch absolute 3 from orderNum_02_cursor into @OrderId while @@fetch_status=0 --提取成功,進行下一條數據的提取操做 begin select @OrderId as id fetch next from orderNum_02_cursor into @OrderId --移動遊標 end
5.利用遊標更新刪除數據
--遊標修改當前數據語法 Update 基表名 Set 列名=值[,...] Where Current of 遊標名 --遊標刪除當前數據語法 Delete 基表名 Where Current of 遊標名
遊標更新刪除當前數據
--1.聲明遊標 declare orderNum_03_cursor cursor scroll for select OrderId ,userId from bigorder where orderNum='ZEORD003402' --2.打開遊標 open orderNum_03_cursor --3.聲明遊標提取數據所要存放的變量 declare @OrderId int ,@userId varchar(15) --4.定位遊標到哪一行 fetch First from orderNum_03_cursor into @OrderId,@userId --into的變量數量必須與遊標查詢結果集的列數相同 while @@fetch_status=0 --提取成功,進行下一條數據的提取操做 begin if @OrderId=122182 begin Update bigorder Set UserId='123' Where Current of orderNum_03_cursor --修改當前行 end if @OrderId=154074 begin Delete bigorder Where Current of orderNum_03_cursor --刪除當前行 end fetch next from orderNum_03_cursor into @OrderId ,@userId --移動遊標 end
6,關閉遊標
遊標打開後,服務器會專門爲遊標分配必定的內存空間存放遊標操做的數據結果集,同時使用遊標也會對某些數據進行封鎖。因此遊標一旦用過,應及時關閉,避免服務器資源浪費。
--關閉遊標語法 close [ Global ] cursor_name | cursor_variable_name --關閉遊標 close orderNum_03_cursor
7,刪除遊標
刪除遊標,釋放資源
--釋放遊標語法 deallocate [ Global ] cursor_name | cursor_variable_name --釋放遊標 deallocate orderNum_03_cursor
使用實例:
USE Test_DB; DECLARE @jid CHAR(5) DECLARE @pic NVARCHAR(64) DECLARE My_Cursor CURSOR --定義遊標 FOR (SELECT jid FROM journal WHERE isall in(1,2)) --查出須要的集合放到遊標中 OPEN My_Cursor; --打開遊標 FETCH NEXT FROM My_Cursor INTO @jid; --讀取第一行數據 WHILE @@FETCH_STATUS = 0 BEGIN SET @pic=(SELECT TOP 1 smallpic FROM journalissue WHERE jid=@jid and (smallpic != '' and smallpic is not null) ORDER BY issueyear DESC,issueno DESC); PRINT (@jid +' '+ @pic); IF(@jid != '' and @jid is not null and @pic != '' and @pic is not null) BEGIN UPDATE journal SET pic=@pic WHERE jid=@jid; END FETCH NEXT FROM My_Cursor INTO @jid; --讀取下一行數據 END CLOSE My_Cursor; --關閉遊標 DEALLOCATE My_Cursor; --釋放遊標 GO
詳細說明
RS.OPEN SQL,CONN,A,B
參數A爲設定遊標的類型,其取值爲: 0 僅向前遊標,只能向前瀏覽記錄,不支持分頁、Recordset、BookMark
1 鍵集遊標,其餘用戶對記錄所作的修改將反映到記錄集中,但其餘用戶增長或刪除記錄不會反映到記錄集中。支持分頁、Recordset、BookMark
2 動態遊標功能最強,但耗資源也最多。用戶對記錄所作的修改,增長或刪除記錄都將反映到記錄集中。支持全功能瀏覽。
3 靜態遊標,只是數據的一個快照,用戶對記錄所作的修改,增長或刪除記錄都不會反映到記錄集中。支持向前或向後移動
參數B爲記錄集的鎖定類型,其取值爲:
1 鎖定類型,默認的,只讀,不能做任何修改
2 當編輯時當即鎖定記錄,最安全的方式
3 只有在調用Update方法時才鎖定記錄集,而在此前的其餘操做仍可對當前記錄進行更改、插入和刪除等
4 當編輯時記錄不會被鎖定,而更改、插入和刪除是在批處理方式下完成的
打開數據記錄集方法其實不止一種,可是咱們用的最多的就是
rs.open sql,1,1的方法,但是後面的數字參數不少人不解其意,下面咱們來介紹一下。
其實open方法後面有多個參數
CursorType LockType CommandType
好比 rs.open sql,1,1
也能夠寫成
rs.cursorType = 1
rs.LockType = 1
rs.open sql
其中CursorType表明從一個表或者一個SQL查詢結果返回的記錄。
這個參數有四個值分別是:
adOpenForwardOnly 表示只容許在記錄集內的記錄間往前移動。這個是缺省值。
adOpenKeyset 反映由其它用戶所作的對記錄的改變或者刪除動做,但並不反映由其它用戶作做的添加新記錄的動做。
adOpenDynamic 反映由其它用戶所作的對記錄的改變或者刪除動做,包括添加的新記錄
adOpenStatic 不反映其它用戶對記錄所作的修改,添加,刪除動做。
這四個值VBSCRIPT預約義位
adOpenForwardOnly = 0
adOpenKeyset = 1
adOpenDynamic = 2
adOpenStatic = 3
lockType 表示當打開記錄集時,數據提供者用於鎖定數據庫的類型:
adLockReadOnly 數據不能改變,這是缺省值!
adLockPessimistic 數據提供者在開始編輯數據的時候鎖定記錄
adLockOptimistic 僅當調用update方法時,數據提供者鎖定記錄
adLockBatchOptimistic 用於批處理修改
他們的常量值定義分別是:
adLockReadOnly = 1
adLockPessimistic = 2
adLockOptimistic = 3
adLockBatchOptimistic = 4
rs.open sql,conn,1,1 讀取記錄 select
rs.open sql,conn,1,3 只更新記錄最好 update
rs.open sql,conn,2,3 插入和刪除最好 insert delete
示例:
1 USE [Community]; 2 3 DECLARE @UserInfoID bigint,@Name nvarchar(100),@UCAccountID bigint; 4 5 6 DECLARE My_Cursor CURSOR --定義遊標 7 FOR ( 8 select distinct(UI.[UserInfoID]),UI.[Name],UIToUC.[UCAccountID] from [dbo].[CMAreaRelation] as AR 9 inner join [dbo].[UserInfo] as UI on UI.[UserInfoID]=AR.[Creator] 10 inner join [dbo].[UserInfoToUCAccount] as UIToUC on UIToUC.[UserInfoID]=UI.[UserInfoID] 11 ) --查出須要的集合放到遊標中 12 OPEN My_Cursor; --打開遊標 13 FETCH NEXT FROM My_Cursor INTO @UserInfoID,@Name,@UCAccountID; --讀取第一行數據 14 WHILE @@FETCH_STATUS = 0 15 BEGIN 16 PRINT '@UserInfoID='+convert(varchar,isnull(@UserInfoID,0))+', @Name='+isnull(@Name,'')+', @UCAccountID='+convert(varchar,isnull(@UCAccountID,0)); --打印,方便查看(正式項目不須要該行) 17 --這裏是根據每一行編寫自定義的操做…… 18 19 FETCH NEXT FROM My_Cursor INTO @UserInfoID,@Name,@UCAccountID; --讀取下一行數據 20 END 21 CLOSE My_Cursor; --關閉遊標 22 DEALLOCATE My_Cursor; --釋放遊標 23 GO