SQL的SELECT FOR UPDATE遊標

遊標SELECT操做將不會對正處理的行執行任何鎖定設置,這使得鏈接到該數據庫的其餘會話能夠改變正在選擇的數據,使用FOR UPDATE子句,在OPEN返回之前的活動集的相應行上會加上互斥鎖,這些鎖會避免其餘的會話對活動集中的行進行更改。直到整個事務被提交爲止。數據庫

示例:性能優化

DECLARE cur CURSOR FOR SELECT * FROM [Table]服務器

FOR UPDATE OF [Table.col]數據結構

 

OPEN cur異步

WHILE @@FETCH_STATUS=0性能

BEGINfetch

    UPDATE [Table] SET [Table.col] WHILE CURRENT OF cur優化

ENDui

CLOSE curspa

DEALLOCATE cur

lock相應狀況:

update, insert ,delete, select ... for update會LOCK相應的ROW 。

只有一個TRANSACTION能夠LOCK相應的行,也就是說若是一個ROW已經LOCKED了,那就不能被其餘TRANSACTION所LOCK了。

LOCK由statement產生但卻由TRANSACTION結尾(commit,rollback),也就是說一個SQL完成後LOCK還會存在,只有在COMMIT/ROLLBACK後纔會RELEASE。

 

SELECT.... FOR UPDATE [OF cols] [NOWAIT];
OF cols
SELECT cols FROM tables [WHERE...] FOR UPDATE [OF cols] [NOWAIT];

前面的FOR UPDATE省略,下面咱們來說一下OF。

 

transaction A運行
select a.object_name,a.object_id from wwm2 a,wwm3 b
2 where b.status='VALID' and a.object_id=b.object_id
3* for update of a.status

則transaction B能夠對b表wwm3的相應行進行DML操做,但不能對a表wwm2相應行進行DML操做.

反一下看看。

 

transaction A運行
select a.object_name,a.object_id from wwm2 a,wwm3 b
2 where b.status='VALID' and a.object_id=b.object_id
3* for update of b.status

則transaction B能夠對a表wwm2的相應行進行DML操做,但不能對b表wwm3相應行進行DML操做.

也就是說LOCK的仍是行,只是若是不加OF的話會對全部涉及的表LOCK的,加了OF後只會LOCK OF 字句所在的TABLE.

 

NOWAIT(若是必定要用FOR UPDATE,我更建議加上NOWAIT)

當有LOCK衝突時會提示錯誤並結束STATEMENT而不是在那裏等待.返回錯誤是"ORA-00054: resource busy and acquire with NOWAIT specified"

另外以下用法也值得推薦,應該酌情考慮使用。

 

FOR UPDATE WAIT 5

 5秒後會出現提示:

 

ORA-30006: resource busy; acquire with WAIT timeout expired
FOR UPDATE NOWAIT SKIP LOCKED;

 

no rows selected
TABLE LOCKS
LOCK TABLE table(s) IN EXCLUSIVE MODE [NOWAIT];

一樣也是在transaction結束時纔會釋放lock。

DEADLOCK:

 

transaction a lock rowA , then transaction b lock rowB
then transaction a tries to lock rowB,
and transaction b tries to lock rowA

也就是說兩個transaction都相互試圖去lock對方已經lock的ROW,都在等待對方釋放本身的lock,這樣就使死鎖。另外,deadlock也會有600提示。

 

版權聲明:本文爲博主原創文章,未經博主容許不得轉載。

 

 

一:認識遊標

遊標(Cursor)它使用戶可逐行訪問由SQL Server返回的結果集。

使用遊標(cursor)的一個主要的緣由就是把集合操做轉換成單個記錄處理方式。

用SQL語言從數據庫中檢索數據後,結果放在內存的一塊區域中,且結果每每是一個含有多個記錄的集合。

遊標機制容許用戶在SQL server內逐行地訪問這些記錄,按照用戶本身的意願來顯示和處理這些記錄。

二:遊標的基本形式

聲明遊標:形式1

DECLARE cursor_name [INSENSITIVE] [SCROLL] CURSOR

FOR select_statement

[FOR {READ ONLY | UPDATE ][OF column_list]}]

形式2

DECLARE cursor_name CURSOR

[LOCAL | GLOBAL]

[FORWARD_ONLY | SCROLL]

[STATIC | KEYSET | DYNAMIC]

[READ_ONLY | SCROLL_LOCKS | OPTIMISTIC]

FOR select_statement

[FOR {READ ONLY | UPDATE ][OF column_list]}]

INSENSITIVE關鍵字指明要爲檢索到的結果集創建一個臨時拷貝,之後的數據從這個臨時拷貝中獲取。

若是在後來遊標處理的過程當中,原有基表中數據發生了改變,那麼它們對於該遊標而言是不可見的。這種不敏感的遊標不容許數據更改。

SCROLL關鍵字指明遊標能夠在任意方向上滾動。全部的fetch選項(first、last、next、relative、absolute)均可以在遊標中使用。

若是忽略該選項,則遊標只能向前滾動(next)。

Select_statement指明SQL語句創建的結果集。Transact SQL語句COMPUTE、COMPUTE BY、FOR BROWSE和INTO在遊標聲明的選擇語句中不容許使用。

READ ONLY指明在遊標結果集中不容許進行數據修改。

UPDATE關鍵字指明遊標的結果集能夠修改。

OF column_list指明結果集中能夠進行修改的列。缺省狀況下(使用UPDATE關鍵字),全部的列均可進行修改。

LOCAL關鍵字指明遊標是局部的,它只能在它所聲明的過程當中使用。

GLOBAL關鍵字使得遊標對於整個鏈接全局可見。全局的遊標在鏈接激活的任什麼時候候都是可用的。只有當鏈接結束時,遊標纔再也不可用。

FORWARD_ONLY指明遊標只能向前滾動。

STATIC的遊標與INSENSITIVE的遊標是相同的。

KEYSET指明選取的行的順序。SQL Server將從結果集中建立一個臨時關鍵字集。若是對數據庫的非關鍵字列進行了修改,則它們對遊標是可見的。

由於是固定的關鍵字集合,因此對關鍵字列進行修改或新插入列是不可見的。

DYNAMIC指明遊標將反映全部對結果集的修改。

SCROLL_LOCK是爲了保證遊標操做的成功,而對修改或刪除加鎖。

OPTIMISTIC指明哪些經過遊標進行的修改或者刪除將不會成功。

注意:

· 若是在SELECT語句中使用了DISTINCT、UNION、GROUP BY語句,且在選擇中包含了聚合表達式,則遊標自動爲INSENSITIVE的遊標。

· 若是基表沒有惟一的索引,則遊標建立成INSENSITIVE的遊標。

· 若是SELECT語句包含了ORDER BY,而被ORDER BY的列並不是惟一的行標識,則DYNAMIC遊標將轉換成KEYSET遊標。

若是KEYSET遊標不能打開,則將轉換成INSENSITIVE遊標。使用SQL ANSI-92語法定義的遊標一樣如此,只是沒有INSENSITIVE關鍵字而已。

打開遊標

打開遊標就是建立結果集。遊標經過DECLARE語句定義,但其實際的執行是經過OPEN語句。語法以下:

OPEN { { [GLOBAL] cursor_name } | cursor_variable_name}

GLOBAL指明一個全局遊標。

Cursor_name是被打開的遊標的名稱。

Cursor_variable_name是所引用遊標的變量名。該變量應該爲遊標類型。

在遊標被打開以後,系統變量@@cursor_rows能夠用來檢測結果集的行數。

@@cursor_rows爲負數時,表示遊標正在被異步遷移,其絕對值(若是@@cursor_rows爲-5,則絕對值爲5)爲當前結果集的行數。

異步遊標使用戶在遊標被徹底遷移時仍然可以訪問遊標的結果。

從遊標中取值

在從遊標中取值的過程當中,能夠在結果集中的每一行上來回移動和處理。

若是遊標定義成了可滾動的(在聲明時使用SCROLL關鍵字),則任什麼時候候均可取出結果集中的任意行。

對於非滾動的遊標,只能對當前行的下一行實施取操做。結果集能夠取到局部變量中。Fetch命令的語法以下:

FETCH [NEXT | PRIOR| FIRST | LAST | ABSOLUTE {n | @nvar} | RELATIVE {n | @nvar}]

FROM [GLOBAL] cursor_name} | cursor_variable_name}

[INTO @variable_name ][,……n]]

NEXT指明從當前行的下一行取值。

PRIOR指明從當前行的前一行取值。

FIRST是結果集的第一行。

LAST是結果集的最後一行。

ABSOLUTE n表示結果集中的第n行,該行數一樣能夠經過一個局部變量傳播。行號從0開始,因此n爲0時不能獲得任何行。

RELATIVE n表示要取出的行在當前行的前n行或後n行的位置上。若是該值爲正數,則要取出的行在當前行前n行的位置上,若是該值爲負數,則返回當前行的後n行。

INTO @cursor_variable_name表示遊標列值存儲的地方的變量列表。

該列表中的變量數應該與DECLARE語句中選擇語句所使用的變量數相同。

變量的數據類型也應該與被選擇列的數據類型相同。直到下一次使用FETCH語句以前,變量中的值都會一直保持。

每一次FETCH的執行都存儲在系統變量@@fetch_status中。

若是FETCH成功,則@@fetch_status被設置成0。@@fetch_status爲-1表示已經到達告終果集的一部分(例如,在遊標被打開以後,基表中的行被刪除)。

@@fetch_status能夠用來構造遊標處理的循環。

關閉遊標

CLOSE語句用來關閉遊標並釋放結果集。遊標關閉以後,不能再執行FETCH操做。若是還須要使用FETCH語句,則要從新打開遊標。語法以下:

CLOSE [GLOBAL] cursor_name | cursor_variable_name

釋放遊標

遊標使用再也不須要以後,要釋放遊標。DEALLOCATE語句釋放數據結構和遊標所加的鎖。語法以下:

DEALLOCATE [GLOBAL] cursor_name | cursor_variable_name

三:遊標的基本使用模板

declare :

        declare  遊標名[scroll]  cursor  for select語句[for update [of列表名]]

        定義一個遊標,使之對應一個select語句

       for update任選項,表示該遊標可用於對當前行的修改與刪除

    open

       打開一個遊標,執行遊標對應的查詢,結果集合爲該遊標的活動集

       open  遊標名

    fetch

       在活動集中將遊標移到特定的行,並取出該行數據放到相應的變量中

       fetch [next | prior | first | last | current | relative n | absolute m] 遊標名into  [變量表]

    close

       關閉遊標,釋放活動集及其所佔資源。須要再使用該遊標時,執行open語句

       close  遊標名

    deallocate

       刪除遊標,之後不能再對該遊標執行open語句

       deallocate 遊標名

    @@FETCH_STATUS

        返回被FETCH 語句執行的最後遊標的狀態.

       0 fetch語句成功

        -1 fetch語句失敗

        -2 被提取的行不存在

例:DECLARE Employee_Cursor CURSOR FORSELECT EmployeeID, Title

FROM AdventureWorks.HumanResources.Employee;

OPEN Employee_Cursor;FETCH NEXT FROM Employee_Cursor;

WHILE @@FETCH_STATUS = 0   

BEGIN      

--//TO DO...

FETCH NEXT FROM Employee_Cursor;   

END;

CLOSE Employee_Cursor;DEALLOCATE Employee_Cursor;

GO

四:遊標性能問題

最好的改進遊標性能的技術就是:能避免時就避免使用遊標,儘量用對應的語句完成相同的功能(通常狀況下,考慮得當效率能大大提高)。

SQL Server是關係數據庫,其處理數據集比處理單行好得多,單獨行的訪問根本不適合關係DBMS。

如有時沒法避免使用遊標,則能夠用以下技巧來優化遊標的性能。

(1). 除非必要不然不要使用static/insensitive遊標。打開static遊標會形成全部的行都被拷貝到臨時表。

這正是爲何它對變化不敏感的緣由——它其實是指向臨時數據庫表中的一個備份。

很天然,結果集越大,聲明其上的static遊標就會引發越多的臨時數據庫的資源爭奪問題。

(2). 除非必要不然不要使用keyset遊標。和static遊標同樣,打開keyset遊標會建立臨時表。

雖然這個表只包括基本表的一個關鍵字列(除非不存在惟一關鍵字),可是當處理大結果集時仍是會至關大的。

(3). 當處理單向的只讀結果集時,使用fast_forward代替forward_only。使

用fast_forward定義一個forward_only,則read_only遊標具備必定的內部性能優化。

(4). 使用read_only關鍵字定義只讀遊標。這樣能夠防止意外的修改,而且讓服務器瞭解遊標移動時不會修改行。

(5). 當心事務處理中經過遊標進行的大量行修改。根據事務隔離級別,這些行在事務完成或回滾前會保持鎖定,這可能形成服務器上的資源爭奪。

(6). 當心動態光標的修改,尤爲是建在非惟一彙集索引鍵的表上的遊標,由於他們會形成「Halloween」問題——對同一行或同一行的重複的錯誤的修改。

由於SQL Server在內部會把某行的關鍵字修改爲一個已經存在的值,並強迫服務器追加下標,使它之後能夠再結果集中移動。

當從結果集的剩餘項中存取時,又會遇到那一行,而後程序會重複,結果形成死循環.

(7). 對於大結果集要考慮使用異步遊標,儘量地把控制權交給調用者。當返回至關大的結果集到可移動的表格時,異步遊標特別有用.

相關文章
相關標籤/搜索