sql-server – 用于FIFO队列的SQL Threadsafe UPDATE TOP 1

前端之家收集整理的这篇文章主要介绍了sql-server – 用于FIFO队列的SQL Threadsafe UPDATE TOP 1前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我准备了一张发票表,然后准备打印.

[STATUS]列是草稿,打印,打印

我需要获取要打印的第一个(FIFO)记录的ID,并更改记录状态.该操作必须是线程安全的,以便另一个进程不会选择相同的InvoiceID

我可以这样做(对我来说看起来很原始,但也许不是……):

1:

WITH CTE AS
(
    SELECT TOP(1) [InvoiceID],[Status]
    FROM    INVOICES
    WHERE   [Status] = 'Print'
    ORDER BY [PrintRequestedDate],[InvoiceID] 
)
UPDATE CTE
SET [Status] = 'Printing',@InvoiceID = [InvoiceID]

…使用@InvoiceID执行操作…

UPDATE  INVOICES
SET [Status] = 'Printed'
WHERE   [InvoiceID] = @InvoiceID

或者我必须使用它(对于第一个声明)

2:

UPDATE INVOICES
SET    [Status] = 'Printing',@InvoiceID = [InvoiceID]
WHERE  [InvoiceID] = 
(
    SELECT TOP 1 [InvoiceID]
    FROM    INVOICES WITH (UPDLOCK)
    WHERE   [Status] = 'Print'
    ORDER BY [PrintRequestedDate],[InvoiceID] 
)

…使用@InvoiceID执行操作……等

(在处理结束之前,即状态最终变为“已打印”时,我无法将状态从更改状态保持为“打印”.

编辑:

如果重要,则DB为READ_COMMITTED_SNAPSHOT

我可以将UPDATE STATUS的交易保持为“打印”并获取ID.但我不能继续保持交易一直打开,将状态更改为“已打印”.这是一个SSRS报告,它对sql进行了几次不同的查询获取发票的各个部分,并且可能会崩溃/无论如何,使事务保持打开状态.

@Gordon Linoff“如果你想要一个队列”FIFO序列并不重要,我只想首先要求打印的发票……“或多或少”(不要有任何不必要的复杂性……)

@Martin Smith“看起来像通常的表作为队列要求” – 是的,正是如此,感谢非常有用的链接.

解:

我采用的解决方案来自评论

@ lad2025指向我SQL Server Process Queue Race Condition使用WITH(ROWLOCK,READPAST,UPDLOCK)和@MartinSmith解释了隔离问题是什么,并指出我在Using tables as Queues – 它正在谈论我正在尝试做什么.

我没有理解为什么UPDATE TOP 1是安全的,UPDATE MyTable SET xxx = yyy WHERE MyColumn =(SELECT TOP 1 SomeColumn FROM SoMetable ORDER BY AnotherColumn)(没有隔离提示)不是,我应该教育自己,但我’我很乐意将隔离提示放在我的代码中并继续使用其他东西:)

谢谢你的帮助.

解决方法

我担心的是重复[InvoiceID]
多个打印请求相同[InvoiceID]

在第一次更新时,ONE行设置[Status] =’Printing’

在第二次更新时,所有[InvoiceID]行都设置为[Status] =’Printed’
这甚至会设置status =’draft’的行

也许这就是你想要的

另一个进程可以在设置[Status] =’Print’之前选择相同的[InvoiceID]

所以有些重复会打印,有些则不打印

我带着关于使用更新锁的评论

这是不确定的,但你可以采取top(1)并跳过订单.您将倾向于获得最新的行但不保证.如果你清除了队列,那么你就可以了.

这表明你可以失去’draft’= 1

declare @invID int; 
declare @T table (iden int identity primary key,invID int,status tinyint);
insert into @T values (1,2),(5,1),(3,(4,(2,(1,2);
declare @iden int;
select * from @t order by iden;

declare @rowcount int = 1; 
while (@ROWCOUNT > 0)
    begin
        update top (1) t 
        set t.status = 3,@invID = t.invID,@iden = t.iden
        from @t t 
        where t.status = '2';
        set @rowcount = @@ROWCOUNT;
        if(@rowcount > 0)
            begin 
                select @invID,@iden;
                -- do stuff  
                update t 
                set t.status = 4
                from @t t
                where t.invID = @invID; -- t.iden = @iden;
                select * from @T order by iden;
            end
    end

猜你在找的MsSQL相关文章