SQL Server 2008:SELECT FOR UPDATE

时间:2012-08-14 10:07:42

标签: sql-server-2008 locking

我在这里看到了一个关于此的问题,但它已经过时了,所以如果现在有解决方案,我会再问一次。

我的问题是这个。我有一个数据库表,我想从中选择,但我想锁定我选择的行。这样做的原因是我可能正在运行另一个进程,它也想要选择相同的行,我想阻止它。

想象一下,我有两个进程在做同样的事情。一个人执行选择并开始执行其数据处理。然后几秒钟后,下一个进程出现并进行选择,但由于行未锁定,因此它也会采用相同的记录并开始处理它们。这当然是一个糟糕的情况。在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

4 个答案:

答案 0 :(得分:8)

您需要使用其中一个所谓的table hints

更新锁可防止其他进程尝试更新或删除相关行 - 但它不会阻止读取访问:

    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

还有一个独占锁,但基本上,更新锁应该足够了。使用更新锁选择行后,这些行将受到“保护”以防止更新和写入,直到您的事务结束。

答案 1 :(得分:3)

通过锁定,您希望第二个进程发生什么?如果您希望它等到第一次完成,您可以使用事务隔离级别完全执行此操作。

尝试运行这个小测试,你就会明白:

在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,您将看到查询已完成

重复测试,设置事务隔离级别读取未提交A,您将看到查询不会被事务锁定

答案 2 :(得分:2)

您应该在事务中包装您的进程,并适当地设置事务隔离级别(即:Serializable)

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中。

其他交易隔离级别可用。

答案 3 :(得分:0)

SQL Server和Oracle的锁定机制完全不同(甚至行为相反)。如果在代码中包含任何事务级别控制元素,那么它就不会是数据库不可知的#34;但是没有任何合理复杂的数据库代码和数据库不可知的#34;无论如何。在这种情况下,你只需要使用"普通的SQL"保持在SQL92规范和控制应用程序端的事务,而不使用" link-to-SQL",但它将限制您编写有效(特定于数据库)解决方案的能力。