[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]
在第一次更新时,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