遊標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]; |
前面的FOR UPDATE省略,下面咱們來說一下OF。
transaction A運行 |
則transaction B能夠對b表wwm3的相應行進行DML操做,但不能對a表wwm2相應行進行DML操做.
反一下看看。
transaction A運行 |
則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 |
no rows selected |
一樣也是在transaction結束時纔會釋放lock。
DEADLOCK:
transaction a lock rowA , then transaction b lock rowB |
也就是說兩個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). 對於大結果集要考慮使用異步遊標,儘量地把控制權交給調用者。當返回至關大的結果集到可移動的表格時,異步遊標特別有用.