sql server:这种嵌套在事务中是否足以从数据库获取唯一的编号?
我想从表中生成一个唯一的编号。 当然,它必须是线程安全的,所以当我检查最后一个数字并得到“3”,然后将“4”存储在数据库中时,我不希望其他人在这两个操作之间(获取数字)并将其存储在数据库中更高的位置)也可以取回“3”,然后也存储“4”
所以我想,将其放入这样的事务中:
begin transaction
declare @maxNum int
select @maxNum = MAX(SequenceNumber) from invoice
where YEAR = @year
if @maxNum is null
begin
set @maxNum = 0
end
set @maxNum = @maxNum + 1
INSERT INTO [Invoice]
([Year]
,[SequenceNumber]
,[DateCreated])
VALUES
(@year
,@maxNum
,GETUTCDATE()
)
commit transaction
return @maxNum
但我想知道,将其放入事务中是否足够? 我的第一个想法是:它会锁定此 sp 供其他人使用,但这是正确的吗? sql server 如何知道第一步要锁定什么?
这种结构能否保证当我更新 @maxnum 值时没有其他人会执行 select @maxnum 部分,并且在那一刻收到相同的 @maxnum
正如我所做的那样,我遇到了麻烦。
我希望您了解我想要实现的目标,也希望您知道我是否确实选择了正确的解决方案。
编辑: 也被描述为“如何单线程存储过程”
i want to generate a unique number from a table.
It has to be thread safe of course, so when i check for the last number and get '3', and then store '4' in the database, i don't want anybody else just in between those two actions (get the number and store it one higher in the database) also to get '3' back, and then also storing '4'
So i thought, put it in a transaction like this:
begin transaction
declare @maxNum int
select @maxNum = MAX(SequenceNumber) from invoice
where YEAR = @year
if @maxNum is null
begin
set @maxNum = 0
end
set @maxNum = @maxNum + 1
INSERT INTO [Invoice]
([Year]
,[SequenceNumber]
,[DateCreated])
VALUES
(@year
,@maxNum
,GETUTCDATE()
)
commit transaction
return @maxNum
But i wondered, is that enough, to put it in a transaction?
my first thought was: it locks this sp for usage by other people, but is that correct? how can sql server know what to lock at the first step?
Will this construction guarantee me that nobody else will do the select @maxnum
part just when i am updating the @maxnum
value, and at that moment receiving the same @maxnum
as i did so i'm in trouble.
I hope you understand what i want to accomplish, and also if you know if i did choose the right solution.
EDIT:
also described as 'How to Single-Thread a stored procedure'
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
如果您想将年份和序列号存储在数据库中,并从中创建发票号,我会使用:
InvoiceYear
列(完全可以计算为YEAR (InvoiceDate)
)InvoiceID INT IDENTITY
列,您可以每年将其重置为 1创建一个计算列
InvoiceNumber
如下:这样,您就可以自动获取发票号码:
当然,如果您需要超过 6 位数字(= 100 万张发票) - 只需调整
InvoiceID
列的RIGHT()
和CAST()
语句。此外,由于这是一个持久计算列,因此您可以为其建立索引以进行快速检索。
这样:您不必担心并发、存储过程、事务和类似的事情 - SQL Server 会为您做这些 - 免费!
If you want to have the year and a sequence number stored in the database, and create an invoice number from that, I'd use:
InvoiceYear
column (which could totally be computed asYEAR(InvoiceDate)
)InvoiceID INT IDENTITY
column which you could reset every year to 1create a computed column
InvoiceNumber
as:This way, you automagically get invoice numbers:
Of course, if you need more than 6 digits (= 1 million invoices) - just adjust the
RIGHT()
andCAST()
statements for theInvoiceID
column.Also, since this is a persisted computed column, you can index it for fast retrieval.
This way: you don't have to worry about concurrency, stored procs, transactions and stuff like that - SQL Server will do that for you - for free!
不,这还不够。 select 设置的共享锁不会阻止任何人同时读取相同的值。
更改为:
这样:
这样您就可以用更新锁替换共享锁,并且两个更新锁彼此不兼容。
holdlock
表示锁定将一直保持到事务结束。所以你仍然需要交易位。请注意,如果还有其他一些过程也想要进行更新,这将无济于事。如果其他过程在不提供 updlock 提示的情况下读取该值,它仍然能够读取计数器的先前值。这可能是一件好事,因为它可以提高其他读者不打算稍后进行更新的情况下的并发性,但它也可能不是您想要的,在这种情况下,要么更新所有过程以使用 updlock< /code>,或使用
xlock
来放置独占锁,与共享锁不兼容。No, it's not enough. The shared lock set by the select will not prevent anyone from reading that same value at the same time.
Change this:
To this:
This way you replace the shared lock with an update lock, and two update locks are not compatible with each over.
The
holdlock
means that the lock is to be held until the end of the transaction. So you do still need the transaction bit.Note that this will not help if there's some other procedure that also wants to do the update. If that other procedure reads the value without providing the
updlock
hint, it will still be able to read the previous value of the counter. This may be a good thing, as it improves concurrency in scenarios where the other readers do not intend to make an update later, but it also may be not what you want, in which case either update all procedures to useupdlock
, or usexlock
instead to place an exclusive lock, not compatible with shared locks.事实证明,我不想锁定表,我只想一次执行一个存储过程。
在 C# 代码中,我会在另一个对象上放置一个锁,这就是这里讨论的内容
http://www.sqlservercentral.com/Forums/Topic357663-8-1.aspx
这就是我所使用的
,其中“holdit1”只是锁的名称。
@result
如果获取锁成功则返回0或1(其中一种是立即成功,另一种是在等待时获取锁)As it turned out, i didn't want to lock the table, i just wanted to execute the stored procedure one at a time.
In C# code i would place a lock on another object, and that's what was discussed here
http://www.sqlservercentral.com/Forums/Topic357663-8-1.aspx
So that's what i used
where 'holdit1' is just a name for the lock.
@result
returns 0 or 1 if it succeeds in getting the lock (one of them is when it immediately succeeds, and the other is when you get the lock while waiting)