1. 程式人生 > >Sql Server遊標的使用

Sql Server遊標的使用

改變 定位 tsql 變量 主鍵 sde enable 三種 cover

Sql Server遊標的使用

一、TSQL和SQL的區別

SQL是Structrued Query Language的縮寫,即結構化查詢語言。1987年,“國際標準化組織(ISO)”把ANSI SQL作為國際標準。這個標準在1992年進行了修訂(SQL-92),1999年再次修訂(SQL-99)。(目前最新的是SQL-2003)(不知道現在情況)

各個廠商在自己的產品中對ANSI的SQL進行擴展,微軟使用的SQL標準稱為Transact SQL,簡稱TSQL;而Oraclec的則稱為PL/SQL。

T-SQL只能在SQL SERVER上使用。它是ANSI SQL的加強版語言、提供了標準的SQL命令。另外,T-SQL還對SQL做了許多補允,提供了數據庫腳本語言,即類似C、Basic和Pascal的基本功能,如變量說明、流控制語言、功能函數等。

PL-SQL(Procedural Language-SQL)是一種增加了過程化概念的SQL語言,是Oracle對SQL的擴充。與標準SQL語言相同,PL-SQL也是Oracle客戶端工具(如SQL*Plus、Developer/2000等)訪問服務器的操作語言。它有標準SQL所沒有的特征:變量(包括預先定義的和自定義的);控制結構(如IF-THEN-ELSE等流控制語句);自定義的存儲過程和函數 ;對象類型等。由於 P/L-SQL 融合了SQL語言的靈活性和過程化的概念,使得P/L-SQL成為了一種功能強大的結構化語言,可以設計復雜的應用。

二、什麽是遊標

遊標其實可以理解成一個定義在特定數據集上的指針,我們可以控制這個指針遍歷數據集,或者僅僅是指向特定的行,所以遊標是定義在以Select開始的數據集上的:

三、遊標的特點

1.在關系型數據庫中,對於查詢的思考是面向集合的。而遊標打破了這一規則,遊標使得我們思考方式變為逐行進行。

2.上面的特點導致了在性能上,遊標會吃更多的內存,減少可用的並發,占用寬帶,鎖定資源,當然還有更多的代碼量。

從遊標對數據庫的讀取方式來說,不難看出遊標為什麽占用更多的資源,就好比從ATM取錢,一次取1000效率肯定比取10次100的效率高。

四、為什麽學習遊標

我個人認為存在既是合理.

1.現存系統有一些遊標,我們查詢必須通過遊標來實現。

2.作為一個備用方式,當我們窮盡了while循環,子查詢,臨時表,表變量,自建函數或其他方式扔來無法實現某些查詢的時候,使用遊標實現。

五、T-SQL中遊標的生命周期以及實現

周期一:定義遊標

T-SQL中的遊標定義在MSDN中如下:

1 DECLARE cursor_name CURSOR [ LOCAL | GLOBAL ] 
2      [ FORWARD_ONLY | SCROLL ] 
3      [ STATIC | KEYSET | DYNAMIC | FAST_FORWARD ] 
4      [ READ_ONLY | SCROLL_LOCKS | OPTIMISTIC ] 
5      [ TYPE_WARNING ] 
6      FOR select_statement 
7      [ FOR UPDATE [ OF column_name [ ,...n ] ] ]
8 [;]

參數說明:

LOCAL和GLOBAL二選一

  LOCAL意味著遊標的生存周期只在批處理或函數或存儲過程中可見,而GLOBAL意味著遊標對於特定連接作為上下文,全局內有效,如果不指定遊標作用域,默認作用域為GLOBAL

FORWARD_ONLY 和 SCROLL 二選一

  FORWARD_ONLY意味著遊標只能從數據集開始向數據集結束的方向讀取,FETCH NEXT是唯一的選項,而SCROLL支持遊標在定義的數據集中向任何方向,或任何位置移動,支持6種移動選項,分別為到第一行(FIRST),最後一行(LAST),下一行(NEXT),上一行(PRIOR),直接跳到某行(ABSOLUTE(n)),相對於目前跳幾行(RELATIVE(n))

STATIC KEYSET DYNAMIC 和 FAST_FORWARD 四選一

這四個關鍵字是遊標所在數據集所反應的表內數據和遊標讀取出的數據的關系

  STATIC意味著,當遊標被建立時,將會創建FOR後面的SELECT語句所包含數據集的副本存入tempdb數據庫中,任何對於底層表內數據的更改不會影響到遊標的內容.

DYNAMIC是和STATIC完全相反的選項,當底層數據庫更改時,遊標的內容也隨之得到反映,在下一次fetch中,數據內容會隨之改變

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不鎖定任何數據,當需要在遊標中更新數據時,如果底層表數據更新,則遊標內數據更新不成功,如果,底層表數據未更新,則遊標內表數據可以更新

周期二:打開遊標

1 OPEN  cursor_name

註意,當全局遊標和局部遊標變量重名時,默認會打開局部變量遊標

周期三:使用遊標

遊標的使用分為兩步,一是操作遊標在數據集內的指向,二是將遊標所指向的行的部分或全部內容進行操作

對於指定 SCROLL選項的遊標來說,支持6種移動選項,分別為到第一行(FIRST),最後一行(LAST),下一行(NEXT),上一行(PRIOR),直接跳到某行(ABSOLUTE(n)),相對於目前跳幾行(RELATIVE(n))

對於未指定 SCROLL選項的遊標來說,只支持NEXT取值

 1 DECLARE cursor_name CURSOR scroll   -----必須指定scroll,否則只支持next只進選項
 2 FOR select PM_Equipments.NameCN from PM_Equipments
 3 open cursor_name                    -----打開遊標
 4 declare @equipments nvarchar(50)    -----聲明變量
 5 fetch next from cursor_name into @equipments     -----取下一行設備名稱,賦值給變量(這就是使用過程:1定位遊標,2賦值操作)
 6 fetch last from cursor_name into @equipments     -----取最後一行設備名稱,賦值給變量
 7 fetch first from cursor_name into @equipments     -----取第一行設備名稱,賦值給變量
 8 fetch prior from cursor_name into @equipments     -----取上一行設備名稱,賦值給變量
 9 fetch absolute 3  from cursor_name into @equipments     -----取第三行設備名稱,賦值給變量
10 fetch relative -1  from cursor_name into @equipments     -----取當前行的上一行設備名稱,賦值給變量(負數向前,正數向後)

遊標經常會和全局變量@@FETCH_STATUS與WHILE循環來共同使用,以達到遍歷遊標所在數據集的目的

@@FETCH_STATUS值有三種情況,分別是0,-1,-2。

0 FETCH 語句成功   
-1 FETCH 語句失敗或此行不在結果集中   
-2 被提取的行不存在

 1     --1.聲明遊標
 2     DECLARE CreateEntry_Cursor cursor scroll
 3     FOR select ProductUniqueCode from dbo.POM_Entry where OrderCode = @OrderCode AND Status = Recovery;
 4     --2.打開遊標
 5     OPEN CreateEntry_Cursor
 6     --3.聲明遊標提取數據所要存放的變量
 7     DECLARE @ProductUniqueCode NVARCHAR(100),@OrderIndex INT = 1
 8     --4.定位遊標到哪一行
 9     FETCH First from CreateEntry_Cursor into @ProductUniqueCode  --into的變量數量必須與遊標查詢結果集的列數相同
10     WHILE @@fetch_status=0  --提取成功,進行下一條數據的提取操作 
11     BEGIN
12         INSERT dbo.POM_Entry
13                 ( Id ,
14                   OrderCode ,
15                   EntryCode ,
16                   ProductUniqueCode ,
17                   CellCode ,
18                   PPRCode ,
19                   Type ,
20                   Status ,
21                   PlanStartTime ,
22                   PlanEndTime ,
23                   OrderIndex,
24                   CreationTime ,
25                   CreateUserId ,
26                   IsEnabled ,
27                   IsDeleted 
28                 )
29         VALUES  ( NEWID() , -- Id - nvarchar(50)
30                   @OrderCode +  - + CONVERT(NVARCHAR(50),@index) , -- OrderCode - nvarchar(50)
31                   [dbo].[f_GetBusinessNo] (EN,01) , -- EntryCode - nvarchar(50)
32                   @ProductUniqueCode , -- ProductUniqueCode - nvarchar(100)
33                   @CellCode , -- CellCode - nvarchar(50)
34                   @PPRCode , -- PPRCode - nvarchar(50)
35                   @Type , -- Type - nvarchar(50)
36                   New , -- Status - nvarchar(50)
37                   @PlanStartTime , -- PlanStartTime - datetime
38                   @PlanEndTime , -- PlanEndTime - datetime
39                   @OrderIndex , -- OrderIndex - int
40                   GETDATE() , -- CreationTime - datetime
41                   @CreateUserId , -- CreateUserId - varchar(50)
42                   1 , -- IsEnabled - bit
43                   0  -- IsDeleted - bit
44                 )
45     FETCH next from CreateEntry_Cursor into @ProductUniqueCode --移動遊標
46     SET @OrderIndex += 1;
47     END 

周期四:關閉遊標

CLOSE test_Cursor -----關閉遊標 

周期五:釋放遊標

1 DEALLOCATE test_Cursor  -----釋放遊標

六、對於遊標一些優化建議

如果能不用遊標,盡量不要使用遊標

用完用完之後一定要關閉和釋放

盡量不要在大量數據上定義遊標

盡量不要使用遊標上更新數據

盡量不要使用insensitive, static和keyset這些參數定義遊標

如果可以,盡量使用FAST_FORWARD關鍵字定義遊標

如果只對數據進行讀取,當讀取時只用到FETCH NEXT選項,則最好使用FORWARD_ONLY參數

Sql Server遊標的使用