在“select top n”查询中返回未锁定的行

时间:2010-09-03 14:57:13

标签: sql-server select locking rows

我需要一个MsSql数据库表和另外8个(相同的)进程并行访问同一个表 - 制作一个select top n,处理这n行,并更新这些行的列。问题是我只需要选择和处理每一行。这意味着如果一个进程到达数据库并选择了前n行,当第二个进程到来时,它应该找到那些被锁定的行并选择从n到2 * n行的行,依此类推......

当你选择它们时,是否有可能锁定某些行,当有人请求锁定的前n行返回下一行,而不是等待锁定的行?看起来像是远射,但是......

我想到的另一件事 - 可能不那么优雅但听起来简单而安全,就是在数据库中有一个计数器,用于在该表上进行选择的实例。第一个实例将递增计数器并选择前n个,下一个将增加计数器并从n *(i-1)到n * i中选择行,依此类推......

这听起来像是一个好的想法吗?你有什么更好的建议吗?任何想法都非常感谢!

感谢您的时间。

4 个答案:

答案 0 :(得分:6)

以下是I blogged about a while ago示例:

READPAST提示是在轮询要处理的记录时确保多个进程不会相互阻塞的原因。另外,在这个示例中,我有一个字段来物理“锁定”记录 - 如果需要可以是日期时间。

DECLARE @NextId INTEGER
BEGIN TRANSACTION

-- Find next available item available
SELECT TOP 1 @NextId = ID
FROM QueueTable WITH (UPDLOCK, READPAST)
WHERE IsBeingProcessed = 0
ORDER BY ID ASC

-- If found, flag it to prevent being picked up again
IF (@NextId IS NOT NULL)
    BEGIN
        UPDATE QueueTable
        SET IsBeingProcessed = 1
        WHERE ID = @NextId
    END

COMMIT TRANSACTION

-- Now return the queue item, if we have one
IF (@NextId IS NOT NULL)
    SELECT * FROM QueueTable WHERE ID = @NextId

答案 1 :(得分:2)

最简单的方法是使用row locking

BEGIN TRAN

SELECT *
FROM authors
WITH (HOLDLOCK, ROWLOCK)
WHERE au_id = '274-80-9391'

/* Do all your stuff here while the record is locked */

COMMIT TRAN

但是,如果您正在访问数据然后关闭连接,那么您将无法使用此方法。

您需要多长时间锁定行?实际上,最好的方法可能是在您选择的行上放置一个计数器(最好使用UPDATE中的OUTPUT子句。

答案 2 :(得分:0)

如果您想以这种方式选择记录,最好的办法是在单独的表格中使用计数器。

您真的不想在生产数据库中专门锁定任何很长一段时间的行,因此我建议您使用计数器。这样,只有一个进程能够一次获取该计数器(因为它会在更新时锁定),这将为您提供所需的并发性。

如果你需要一只手来编写可以做到这一点的表格和程序(简单而安全地进行操作!),只需要问。

答案 3 :(得分:0)

编辑:啊,没关系,你的工作是断断续续的。怎么样:

UPDATE TOP (@n) QueueTable SET Locked = 1
OUTPUT INSERTED.Col1, INSERTED.Col2 INTO @this
WHERE Locked = 0

<do your stuff>

也许您正在寻找READPAST提示?

<begin or save transaction>

INSERT INTO @this (Col1, Col2) 
SELECT TOP (@n) Col1, Col2 
FROM Table1 WITH (ROWLOCK, HOLDLOCK, READPAST)

<do your stuff>

<commit or rollback>