2012-08-14 113 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”,但会限制您编写有效(数据库特定)解决方案的能力。