SQL Cursor(遊標)--轉

以前作東西的時候遇到須要用有標的, 當時就仿照着別人的例子寫的, 最近忽然想起來, 就想系統的看一下游標。html

原文連接數據庫

定義概念性能優化

遊標是處理數據的一種方法,爲了查看或者處理結果集中的數據, 遊標提供了在結果集中一次一行或者多行前進或者向後瀏覽數據的能力。能夠吧遊標看成一個指針,他能夠指定結果中的任何位置,而後容許用戶對指定位置的數據進行處理。服務器

使用規範及示例性能

1.聲明遊標fetch

遊標主要包含遊標結果集合遊標位置兩個部分,遊標結果集是定義遊標的select語句返回的行集合,遊標的位置則是指向這個結果集中的某一行指針。優化

使用遊標以前, 必需要先聲明遊標, SQL Server中聲明使用declare cursor 語句,聲明遊標包含定義遊標的滾動行爲和用戶生成遊標所操做的結果集的查詢:ui

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] ] ]

說明:spa

cursor_name:是所定義的T_SQL 服務器遊標的名稱。指針

LOCAL:對於在其中建立批處理、存儲過程或觸發器來講,該遊標的做用域是局部的。

GLOBAL:指定該遊標的做用域是全局的

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 T_SQL遊標支持FORWARD_ONLY。

STATIC:定義一個遊標,以建立將又該遊標使用的數據臨時複本,對遊標的全部請求都從tempdb中的這以臨時表中不獲得應答;所以,在對該遊標進行提取操做時返回的數據中不反映對基表所作的修改,而且該遊標不容許修改。

KEYSET:指定當遊標打開時,遊標重的行的成員身份和順序已經固定。對行進行惟一標識的鍵值內置在tempdb內一個稱爲keyset的表中。

DYNAMIC:定義一個遊標,以反映在滾動遊標時對結果集內的各行所作的全部數據更改。行的數據值、順序和成員身份在每次提取時都會更改,動態遊標不支持ABSOLUTE提取選項。

FAST_FORWARD:指定啓動了性能優化的FORWARD_ONLY、READ_ONLY遊標。若是指定了SCROLL或FOR_UPDATE,則不能指定FAST_FORWARD。

SCROLL_LOCKS:指定經過遊標進行的定位更新或刪除必定會成功。將行讀入遊標時SQL Server將鎖定這些行,以確保隨後可對它們進行修改,若是還指定了FAST_FORWARD或STATIC,則不能指定SCROLL_LOCKS。

OPTIMISTIC:指定若是行自讀入遊標以來已獲得更新,則經過遊標進行的定位更新或定位刪除不成功。當將行讀入遊標時,SQL Server不鎖定行,它改用timestamp列值比較結果來肯定行讀入遊標後是否發生了修改,若是表不包含timestamp列,它改用校驗和值進行肯定,若是以修改該行,則嘗試進行的定位更新或刪除將失敗,若是還指定了FAST_FORWARD,則不能指定OPTIMISTIC。

TYPE_WARNING:指定遊標從所請求的類型隱式轉換爲另外一種類型時,向客戶端發送警告消息。

select_statement:是定義遊標結果集中的標準SELECT語句。

 2.打開遊標

使用遊標以前, 須要打開遊標:

OPEN [ GLOBAL ] cursor_name | cursor_variable_name;

說明:

GLOBAL:指定cursor_name是全局遊標。

cursor_name:已聲明的遊標的名稱。若是全局遊標和局部遊標都使用cursor_name做爲其名稱,那麼若是指定了GLOBAL,則cursor_name指的是全局遊標,不然cursor_name指的是局部遊標。

cursor_variable_name:遊標變量的名稱。

3.讀取遊標中的數據

打開遊標以後就能夠讀取遊標中的數據了, fetch 命令能夠讀取遊標中的某一行數據,fetch的語法:

ETCH 
[ [ NEXT | PRIOR | FIRST | LAST 

         | ABSOLUTE { n | @nvar }

         | RELATIVE { n | @nvar }
    ]
FROM
]
{ { [GLOBAL ] cursor_name } | @cursor_variable_name}
[ INTO @variable_name [ ,...n ] ]

說明:

NEXT: 緊跟當前行返回結果行,而且當前行遞增爲返回行,若是FETCH NEXT爲對遊標的第一次提取操做,則返回結果集中的第一行。NEXT爲默認的遊標提取選項。

PRIOR:返回緊鄰當前行前面的結果行,而且當前行遞減爲返回行,若是FETCH PRIOR爲對遊標的第一次提取操做,則沒有行返回而且遊標置於第一行以前。

FIRST:返回遊標中的第一行並將其做爲當前行。

LAST:返回遊標中的最後一行並將其做爲當前行。

ABSOLUTE { n | @nvar }:若是n或@nvar爲正,則返回從遊標頭開始向後n行的第n行,並將返回行變成新的當前行。若是n或@nvar爲負,則返回從遊標末尾開始向前的n行的第n行,並將返回行變成新的當前行。若是n或@nvar爲0,則不返回行。n必須是整數常量,而且@nvar的數據類型必須爲int、tinyint或smallint.

RELATIVE { n | @nvar }:若是n或@nvar爲正,則返回從當前行開始向後的第n行。若是n或@nvar爲負,則返回從當前行開始向前的第n行。若是n或@nvar爲0,則返回當前行,對遊標第一次提取時,若是在將n或@nvar設置爲負數或0的狀況下指定FETCH RELATIVE,則不返回行,n必須是整數常量,@nvar的數據類型必須是int、tinyint或smallint.

GLOBAL:指定cursor_name是全局遊標。

cursor_name:已聲明的遊標的名稱。若是全局遊標和局部遊標都使用cursor_name做爲其名稱,那麼若是指定了GLOBAL,則cursor_name指的是全局遊標,不然cursor_name指的是局部遊標。

@cursor_variable_name:遊標變量名,引用要從中進行提取操做的打開的遊標。

INTO @variable_name [ ,...n ]:容許將提取操做的列數據放到局部變量中。列表中的各個變量從左到右與遊標結果集中的相應列相關聯。各變量的數據類型必須與相應的結果集列的數據類型相匹配,或是結果集列數據類型所支持的隱士轉換。變量的數目必須與遊標選擇列表中的列數一致

4.關閉遊標

SQL Server 在打開遊標以後, 服務器會專門爲遊標開闢必定的內存空間存放遊標操做的數據結果集,同時遊標的使用也會根據具體狀況對某些數據進行封鎖。因此在不使用遊標的時候,能夠將其關閉 ,已釋放遊標所佔用的服務器資源,關閉遊標使用close 語句。

CLOSE [ GLOBAL ] cursor_name | cursor_variable_name

5.釋放遊標

遊標操做的結果集空間雖然釋放了,可是遊標自己也會佔用必定的計算集資源,因此在使用完遊標以後, 爲了收回被遊標佔用的資源, 應該將遊標釋放。釋放遊標使用deallocate語句。

DEALLOCATE [GLOBAL] cursor_name | @ccursor_variable_name

@ccursor_variable_name:遊標變量的名稱,@ccursor_variable_name必須爲cursor類型。

DEALLOCATE @ccursor_variable_name 語句只刪除對遊標變量名稱的引用,直到批處理、存儲過程或觸發器結束時變量離開做用域,才釋放變量。

示例:

        DECLARE BookOID_Cursor CURSOR
        FOR
            SELECT  OID
            FROM    T_Book_TX
            WHERE   ReceivedDate < DATEADD(DAY, -@Xdays, GETDATE());
        OPEN BookOID_Cursor;
        FETCH NEXT FROM BookOID_Cursor INTO @BookOID;
        WHILE @@FETCH_STATUS = 0
            BEGIN
                EXEC P_UpdateBookByOID @BookOID;
                FETCH NEXT FROM BookOID_Cursor INTO @BookOID;
            END;
        CLOSE BookOID_Cursor;
        DEALLOCATE BookOID_Cursor;

網上示例:

1.使用遊標變量

聲明變量用DECLARE,爲變量賦值能夠用set或SELECT語句,對於遊標變量的聲明和賦值,其操做基本相同。在具體使用時,首先要建立一個遊標,將其打開後,將遊標的值賦給遊標變量,並經過FETCH語句從遊標變量中讀取值,最後關閉釋放遊標。

【例】聲明名稱爲@varCursor的遊標變量,輸入以下:

DECLARE @varCursor Cursor --聲明遊標變量

DECLARE cursor_fruit CURSOR FOR --建立遊標

SELECT f_name,f_price FROM fruits;

OPEN cursor_fruit --打開遊標

SET @varCursor=cursor_fruit --爲遊標變量賦值

FETCH NEXT FROM @varCursor --從遊標變量中讀取值

WHILE @@FETCH_STATUS=0 --判斷FETCH語句是否執行成功

BEGIN

FETCH NEXT FROM @varCursor --讀取遊標變量中的數據

END

CLOSE @varCursor --關閉遊標

DEALLOCATE @varCursor; --釋放遊標

2.用遊標爲變量賦值

在遊標的操做過程當中,可使用FETCH語句將數據值存入變量,這些保持表中列值的變量能夠在後面的程序中使用。

【例】建立遊標cursor_variable,將fruits表中的記錄f_name,f_price值賦給變量@fruitName和@fruitPrice,並打印輸出。

--建立遊標cursor_variable,將fruits表中的記錄f_name,f_price值賦給變量@fruitName和@fruitPrice
DECLARE @fruitName varchar(50),@fruitPrice Decimal(8,2)
DECLARE cursor_variable CURSOR FOR
SELECT f_name,f_price FROM fruits WHERE f_id<100;
OPEN cursor_variable
FETCH NEXT FROM cursor_variable
INTO @fruitName,@fruitPrice
PRINT 'Fruit Name and Fruit Price is:'
WHILE @@FETCH_STATUS=0
BEGIN
   PRING  @fruitName+'  '+STR(@fruitPrice,8,2)
FETCH NEXT FROM cursor_variable
INTO @fruitName,@fruitPrice
END
CLOSE cursor_variable;
DECALLOCATE cursor_variable;

3.用ORDER BY 子句改變遊標中的執行順序

遊標是一個查詢結果集,那麼能不能對結果進行排序呢?答案是否認的。與基本的SELECT語句中的排序方法相同,ORDER BY子句添加到查詢中能夠對遊標查詢的結果排序。

注意:只有出如今遊標中的SELECT語句中的列才能做爲ORDER BY 子句的排序列,而對與非遊標的SELECT語句中,表中任何列均可以做爲ORDER BY 的排序列,即便該列沒有出如今SELECT語句的查詢結果列中。

【例】聲明名稱爲cursor_order的遊標,對fruits表中的記錄按照價格字段降序排列,輸入語句以下: 

--建立遊標cursor_order的遊標,將fruits表中的記錄按照價格排序

DECLARE cursor_order CURSOR FOR
SELECT f_name,f_price FROM fruits WHERE f_id<100 ORDER BY f_price DESC;
OPEN cursor_order
FETCH NEXT FROM cursor_order
WHILE @@FETCH_STATUS=0
FETCH NEXT FROM cursor_order
CLOSE cursor_variable
DECALLOCATE cursor_variable

4.用遊標修改數據

【例】聲明整型變量@fid=99,而後聲明一個對fruits表進行操做的遊標,打開該遊標,使用FETCH NEXT方法來獲取遊標中的每一行的數據,若是獲取到的記錄的f_id的字段值與@sid值相同,將f_id=@fid的記錄中的f_price修改成500,最後關閉釋放遊標,輸入以下:

--使用FETCH NEXT 方法來獲取遊標中的每一行數據
--若是獲取到的記錄的f_id 與@id相同,則將價格改成500
DECLARE @id INT = 99,@fid INT
DECLARE cursor_fruit CURSOR FOR
SELECT f_id FROM fruits;
OPEN cursor_fruit
FETCH NEXT FROM cursor_fruit
INTO @fid
WHILE @@FETCH_STATUS=0
BEGIN
  IF @fid=@id
    update fruits set f_price=500 where f_id=@id
FETCH NEXT FROM cursor_variable
INTO @fid
END
CLOSE cursor_fruit;
DECALLOCATE cursor_fruit;

5.用遊標刪除數據

使用遊標刪除數據時,既能夠刪除遊標結果集中的數據,也能夠刪除基本表中的數據

【例】使用遊標刪除fruits表中s_id=102的記錄,以下:

--【例】使用遊標刪除fruits表中s_id=102的記錄,以下
DECLARE @sid1 INT,@id1 int=102
DECLARE cursor_delete CURSOR FOR
SELECT s_id FROM fruits;
OPEN cursor_delete
FETCH NEXT FROM cursor_delete INTO @sid1
WHILE @@FETCH_STATUS=0
BEGIN
    IF @sid1=@id1
BEGIN 
    DELETE FROM fruits where s_id=@id1
END
FETCH NEXT FROM cursor_delete INTO @sid1
END
CLOSE cursor_delete
DEALLOCATE cursor_delete;
SELECT * FROM fruits where s_id=102;
相關文章
相關標籤/搜索