使用锁定进行T-SQL选择和更新 - 事务或表提示

时间:2017-03-26 00:22:27

标签: sql-server tsql transactions locking hint

如何实现以下事务锁定?

在大简化中 - 我有一个带有状态(创建,已启动,已完成)的“任务”表。我想创建存储过程GetNext以获取尚未启动的前1个任务(具有Created状态)。

在此过程中,我想将任务标记为Started。显然,当两个进程调用此过程并获得相同的任务时,我希望避免这种情况。

不会经常调用该过程,因此性能不是问题,保持数据不被破坏是一个问题。

所以我想做这样的事情:

UPDATE tblTasks 
SET Status = 'Started' 
WHERE TaskId = (SELECT TOP 1 TaskId 
                FROM tblTasks 
                WHERE Status = 'Created')

我也希望收到我刚刚更新的任务,而不是上面我需要的东西:

DECLARE @TaskId AS INT = (SELECT TOP 1 TaskId FROM tblTasks WHERE Status = 'Created')

UPDATE tblTasks 
SET Status = 'Started' 
WHERE TaskId = @TaskId
[... - Do something with @TaskId - not relevant]

OR

DECLARE @TaskIds AS TABLE(Id INT)

UPDATE tblTasks 
SET Status = 'Started' 
OUTPUT INSERTED.Id INTO @TaskIdS
WHERE TaskId = @TaskId
[... - Do something with @TaskIds - not relevant]

假设我需要select + update来实现我的需求 - 我怎样才能确保在现有流程完成之前,其他流程都不会执行第一次操作(select)?

据我所知,甚至Serializable隔离级别的事务在这里还不够,因为其他进程可以读取数据,然后等到我完成(因为它的更新被锁定)并更新我刚刚更新的数据。 / p>

我觉得表提示XLOCKHOLDLOCK可能会有所帮助,但我不是专家,MS医生让我害怕:

  

注意
  由于SQL Server查询优化器通常会为查询选择最佳执行计划,因此我们建议有经验的开发人员和数据库管理员仅将提示用作最后的手段。

(来自https://docs.microsoft.com/en-us/sql/t-sql/queries/hints-transact-sql-table

那么我如何确保两个进程不会更新一个项目呢?如何确保如果一个进程正在运行另一个进程将等待并在第一次完成后执行其工作而不是失败?

1 个答案:

答案 0 :(得分:0)

通常,SQL Server会自动锁定每个步骤,直到您拥有GO或到达脚本的末尾。

根据我的理解,您想要/需要的是SELECT/UPDATE"一气呵成的方式"。您应该能够通过TRANSACTIONTRY ... CATCH和CTE的组合来实现这一目标。

DECLARE @TaskIds AS TABLE (TaskId INT);

BEGIN TRANSACTION;
    BEGIN TRY
      WITH myTasks (TaskId) AS (
         SELECT TOP 1 t.TaskId
           FROM tblTasks AS t
          WHERE t.Status = 'Created'
      )
      UPDATE t
         SET t.Status = 'Started'
      OUTPUT INSERTED.TaskId INTO @TaskIds
        FROM tblTasks AS t
       INNER JOIN myTasks AS mt
               ON mt.TaskId = t.TaskId;
      END TRY
    BEGIN CATCH
          IF @@TRANCOUNT > 0
             ROLLBACK TRANSACTION;
          THROW;
      END CATCH;

IF @@TRANCOUNT > 0
BEGIN
    COMMIT TRANSACTION;
    SELECT TaskId FROM @TaskIds;
    [.. do other stuff ..]
  END

GO