【问题标题】:SQL Server 2008: SELECT FOR UPDATESQL Server 2008:选择更新
【发布时间】:2016-09-23 02:17:19
【问题描述】:

我在这里看到了一个关于这个的问题,但是它很旧,所以如果现在存在解决方案,我会再次询问。

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

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

【问题讨论】:

  • 你可以使用lock hint - select * from tbl (updlock)
  • 使用 updlock 或 xlock 提示(通常 - updlock,但这取决于具体的任务)。
  • 这如何应用于我添加到问题中的陈述?
  • 看到你的最终编辑后,一旦发现资源被锁定,使用 nowait 返回:select * from tbl (updlock, nowait)
  • 谢谢,我会试试的

标签: sql-server-2008 locking


【解决方案1】:

你需要使用所谓的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

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

【讨论】:

  • 看来你放错了 WITH 子句(它应该在 FROM 部分)。
【解决方案2】:

通过锁定,您希望第二个进程发生什么?如果您希望它等到第一个完成,您完全可以使用事务隔离级别来做到这一点。

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

在 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 上设置事务隔离级别 read uncommitted 重复测试,您将看到查询不会被事务锁定

【讨论】:

  • 您好,感谢您的回复。我希望第二个过程根本不允许访问。我不希望它等待第一个进程释放锁。例如,我宁愿它返回一个资源繁忙异常,这样我就可以自己处理它,然后等待一两分钟再次运行该过程。我在上面添加了我的选择。
【解决方案3】:

您应该将您的流程包装在一个事务中,并适当地设置事务隔离级别(即: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 中。

其他事务隔离级别可用。

【讨论】:

  • 我应该补充一点,我只能使用标准的 sql 语句。我无权访问过程、函数等。它必须通过一个简单的语句来完成。这是一个很长的故事和一个设计考虑,已经脱离了我的掌控。我会将这些信息添加到我最初的问题中。
  • @CSharpened 您可以在 C# 代码中初始化事务(通过 SqlConnection.BeginTransaction 或 System.Transactions 命名空间,具体取决于您的基础架构) - 并在该语句中设置 Data.IsolationLevel - 请参阅 @987654321 @例如
  • Oracle 或其他数据库的事务是否也是如此?我只是问,因为我的解决方案被设计为与数据库无关,因此我无法对任何特定于数据源的内容进行硬编码。我又一次做出了另一个决定:)
【解决方案4】:

SQL Server 和Oracle 的锁定机制完全不同(甚至在行为上相反)。如果您在代码中包含任何事务级控制元素,它将不会是“数据库不可知论”,但无论如何都不会有任何合理复杂性的数据库代码“数据库不可知论”。在这种情况下,您必须仅使用“纯 SQL”保持在 SQL92 规范内并在应用程序端控制事务,而不使用“链接到 SQL”,但这将限制您编写有效(特定于数据库)解决方案的能力。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-01
    • 1970-01-01
    • 2011-09-07
    相关资源
    最近更新 更多