我可以同时选择和更新吗?

时间:2010-02-19 13:52:55

标签: sql-server

这是对我正在做的工作的过度简化的解释 我有一个状态列表。应用程序的多个实例将提取状态为NEW的第一行内容,将状态更新为WORKING,然后继续处理内容。
使用两个数据库调用很容易做到这一点;首先是SELECT,然后是UPDATE。但我希望在一次调用中完成所有操作,以便应用程序的另一个实例不会拉同一行。有点像SELECT_AND_UPDATE

存储过程是最好的方法吗?

6 个答案:

答案 0 :(得分:8)

您可以使用OUTPUT声明。

DECLARE @Table TABLE (ID INTEGER, Status VARCHAR(32))
INSERT INTO @Table VALUES (1, 'New')
INSERT INTO @Table VALUES (2, 'New')
INSERT INTO @Table VALUES (3, 'Working')

UPDATE  @Table
SET     Status = 'Working'
OUTPUT  Inserted.*
FROM    @Table t1
        INNER JOIN (
          SELECT  TOP 1 ID 
          FROM    @Table
          WHERE   Status = 'New'
        ) t2 ON t2.ID = t1.ID

答案 1 :(得分:2)

听起来像是一个队列处理场景,您只希望一个进程获取给定的记录。

如果是这种情况,请查看我今天早些时候提供的答案,该答案描述了如何使用事务结合UPDLOCK和READPAST表提示来实现此逻辑: Row locks - manually using them

最好包裹在sproc中。

我不确定这是你想要做什么,所以我没有投票决定关闭。

答案 2 :(得分:1)

不完全,但您可以随后SELECT ... WITH (UPDLOCK),然后UPDATE..。这与原子操作一样好,因为它告诉数据库您将要更新之前选择的内容,因此它可以锁定这些行,从而防止与其他客户端发生冲突。在Oracle和其他一些数据库(MySQL认为)下,语法为SELECT ... FOR UPDATE

注意:我认为您需要确保在事务中发生两个语句才能使其正常工作。

答案 3 :(得分:1)

你应该在这里做三件事:

  1. 锁定您正在处理的行
  2. 确保此行仅锁定此行
  3. 不要等待锁定的记录:改为跳过下一个记录。
  4. 为此,您只需发出以下信息:

    SELECT  TOP 1 *
    FROM    mytable (ROWLOCK, UPDLOCK, READPAST)
    WHERE   status = 'NEW'
    ORDER BY
            date
    
    UPDATE  …
    

    在交易中。

答案 4 :(得分:1)

存储过程是可行的方法。你需要看看交易。 Sql server就是为这种事而诞生的。

答案 5 :(得分:1)

是的,并且可能使用rowlock提示使其与其他线程隔离,例如

UPDATE
Jobs WITH (ROWLOCK, UPDLOCK, READPAST)
SET Status = 'WORKING'
WHERE JobID =
(SELECT Top 1 JobId FROM Jobs WHERE Status = 'NEW')

编辑:Quassnoi建议Rowlock会更好,但同样的想法适用于在一个查询中进行更新。