2012-08-14 106 views
4

我在這裏看到了一個關於這個問題的問題,但它已經很老了,所以如果現在有解決方案,我會再問一次。SQL Server 2008:SELECT FOR UPDATE

我的問題是這樣的。我有一個數據庫表,我希望從中選擇,但我想鎖定我選擇的行。這樣做的原因是我可能有另一個正在運行的進程也想要選擇相同的行,我想要阻止它。

想象一下,我有兩個進程在做同樣的事情。一個執行選擇並開始執行其數據處理。然後幾秒鐘後,下一個過程就會出現並執行一個選擇,但由於行未鎖定,它也會採用相同的記錄並開始處理它們。這當然是一個糟糕的情況。在Oracle中,您可以使用SELECT FOR UPDATE,它將取消行的鎖定,以防止它們被第二個進程使用。 SQL Server 2008中如何實現這一點?

我應該補充說我只能使用標準的sql語句。我無法訪問程序,函數等。它必須通過一個簡單的語句來完成。它是一個漫長的故事和一個設計思想,已經從我的手中拿走了。該解決方案必須能夠存儲在表中,稍後檢索,然後通過C#中的ADO對象運行,特別是分配給命令對象。

如何將鎖應用於此聲明?

SELECT * 
FROM 
    (SELECT TOP (20) * 
    FROM [TMA_NOT_TO_ENTITY_QUEUE] 
    WHERE [TMA_NOT_TO_ENTITY_QUEUE].[STATE_ID] = 2 
    ORDER BY TMA_NOT_TO_ENTITY_QUEUE.ID) a 
+1

您可以使用[鎖定提示](http://msdn.microsoft.com/zh-cn/library/ms187373。aspx) - 'select * from tbl(updlock)' – 2012-08-14 09:29:46

+2

在看到最終編輯後,立即使用nowait在資源被鎖定時立即返回:'select * from tbl(updlock,nowait)' – 2012-08-14 11:22:14

+0

謝謝我將給出試一試 – CSharpened 2012-08-14 12:03:59

回答

8

您需要使用的所謂table hints之一:

更新鎖防止其他進程試圖UPD吃或有關刪除行 - 但它不會阻止讀取訪問:

SELECT TOP (20) * 
    FROM [TMA_NOT_TO_ENTITY_QUEUE] WITH (UPDLOCK) 
    WHERE [TMA_NOT_TO_ENTITY_QUEUE].[STATE_ID] = 2 
    ORDER BY TMA_NOT_TO_ENTITY_QUEUE.ID 

還有一個獨佔鎖,但基本上,更新鎖應該是足夠的。一旦你用更新鎖選擇了你的行,那些行就會被「保護」,以防止更新和寫入,直到事務結束。

+0

看起來你放錯了WITH子句(它應該在FROM部分)。 – alexey 2014-09-03 13:11:14

+0

@alexey:絕對正確 - 修復它 - 謝謝! – 2014-09-03 14:18:27

2

你應該換一個交易的過程,並設置適當的事務隔離級別(即:序列化)

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE 
BEGIN TRAN 
    UPDATE yourtable... 
    -- process 1 
COMMIT TRAN 

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE 
BEGIN TRAN 
    UPDATE yourtable... 
    -- process 2 
COMMIT TRAN 

此行爲已在SQL Server自遠古以來。

其他事務隔離級別可用。

+0

我應該補充說我只能使用標準的sql語句。我無法訪問程序,函數等。它必須通過一個簡單的語句來完成。它是一個漫長的故事和一個設計思想,已經從我的手中拿走了。我會將這些信息添加到我最初的問題中。 – CSharpened 2012-08-14 09:27:41

+0

@CSharpened您可以在C#代碼中初始化一個事務(通過SqlConnection.BeginTransaction或System.Transactions命名空間,具體取決於您的基礎架構) - 並在該語句中設置Data.IsolationLevel - 請參閱http://msdn.microsoft.com /en-us/library/5ha4240h.aspx例如 – podiluska 2012-08-14 09:32:43

+0

同樣用於Oracle或其他數據庫的事務嗎?我只問,因爲我的解決方案被設計爲數據庫不可知的,所以我不能硬編碼任何特定的數據源。再次從我的手中取出另一個決定:) – CSharpened 2012-08-14 09:36:11

3

by lock,你想在第二個過程中發生什麼?如果你希望它等到第一個完成,你可以完全使用事務隔離級別來完成。

嘗試運行這個小測試,你就會明白:

打開兩個新的查詢上SSMS(可以從現在開始一個稱之爲A和B)和A,創建一個簡單的表是這樣的:

create table transTest(id int) 
insert into transTest values(1) 

現在,請執行下列操作:

在兩人面前做select * from transTest。您將看到值1

上運行:

set transaction isolation level read committed 

在B都正常運行:

begin transaction 
insert into transTest values(2) 

上運行:

select * from transTest 

你會發現查詢不會完成,因爲它被交易鎖定B

在B都正常運行:

commit transaction 

回到A,你會看到,查詢完成

重複同一套事務隔離級別的測試上的一個未提交讀,你會看到,查詢不會被鎖定由事務

+0

嗨,感謝您的回覆。我希望第二個過程不被允許訪問。我不希望它等待第一個進程釋放鎖。我寧願它返回一個資源繁忙的異常,例如,我可以自己處理它,然後等待一兩分鐘再次運行該過程。我在上面添加了我的選擇。 – CSharpened 2012-08-14 09:39:49

0

SQL Server和Oracle的鎖定機制完全不同(甚至在行爲上相反)。如果在代碼中包含任何事務級別的控制元素,它將不會是「數據庫不可知的」,但不管數據庫代碼是否具有「數據庫無關」的任何合理的複雜性。在這種情況下,您只需使用「純SQL」保留在SQL92規範和應用程序端的控制事務中,而不使用「鏈接到SQL」,但會限制您編寫有效(數據庫特定)解決方案的能力。