在 SQL Server 中获取完全连续的 int 值的最佳方法
我有一个业务要求,即我的 Invoices
表中的 InvoiceNumber
字段必须完全连续 - 没有间隙,否则审计员可能会认为我们的会计师在做一些可疑的事情!
我的第一个想法是简单地使用主键(身份),但如果事务回滚,序列中就会出现间隙。
因此,我的第二个想法是使用一个触发器,在插入时查找表中最高的 InvoiceNumber
值,为其加 1,并将其用作 InvoiceNumber
code> 为新行。易于实施。
近乎同步的插入是否存在潜在问题?例如,两个几乎同时运行触发器的插入是否可能会获得相同的“当前最高的 InvoiceNumber
”值,从而插入具有相同 InvoiceNumber
的行?
还有其他我可能遗漏的问题吗?另一种方法会更好吗?
I have a business requirement that the InvoiceNumber
field in my Invoices
table be totally sequential - no gaps or the auditors might think our accountants are up to something fishy!
My first thought was to simply use the primary key (identity) but if a transaction is rolled back a gap appears in the sequence.
So my second thought is to use a trigger which, at the point of insert, looks for the highest InvoiceNumber
value in the table, adds 1 to it, and uses it as the InvoiceNumber
for the new row. Easy to implement.
Are there potential issues with near-simultaneous inserts? For example, might two near simultaneous inserts running the trigger at the same time get the same 'currently highest InvoiceNumber
' value and therefore insert rows with the same InvoiceNumber
?
Are there other issues I might be missing? Would another approach be better?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
创建一个记录“计数器”的表。
对于您的发票,您可以向该表添加一些记录,以跟踪必须使用的下一个整数。
创建发票时,您应该使用该值并增加它。当您的事务回滚时,对该计数器的更新也将回滚。 (确保对该表加锁,以确保没有其他进程可以使用相同的值)。
这比查看发票表中使用的最高当前计数器可靠得多。
Create a table which keeps tracks of 'counters'.
For your invoices, you can add some record to that table which keeps track of the next integer that must be used.
When creating an invoice, you should use that value, and increase it. When your transaction is rolled back, the update to that counter will be rollbacked as well. (Make sure that you put a lock on that table, to be sure that no other process can use the same value).
This is much more reliable than looking at the highest current counter that is being used in your invoice table.
如果从表中删除数据,您可能仍然会出现间隙。但如果数据只进不出,那么通过正确使用外部序列表上的事务,应该可以很好地做到这一点。不要使用 MAX()+1,因为它可能存在计时问题,或者您可能必须锁定比所需更多的表(页/表)。
拥有一张只有一个记录和一列的顺序表。以原子方式从表中检索数字,将检索和使用包装在单个事务中。
UPDATE语句是原子的,不能被中断,并且双重赋值使@next的值成为原子的。它相当于在 SQL Server 2005+ 中使用 OUTPUT 子句返回更新后的值。如果您一次性需要一系列数字,则使用 PRE-update 值比 POST-update 值更容易,即
参考 SQL Server UPDATE 语句
You may still get gaps if data gets deleted from the table. But if data only goes in and not out, then with proper use of transactions on an external sequence table, it should be possible to do this nicely. Don't use MAX()+1 because it can have timing issues, or you may have to lock more of the table (page/table) than required.
Have a sequential table that has only one single record and column. Retrieve numbers from the table atomically, wrapping the retrieval and usage in a single transaction.
The UPDATE statement is atomic and cannot be interrupted, and the double assignment make the value of @next atomic. It is equivalent to using an OUTPUT clause in SQL Server 2005+ to return the updated value. If you need a range of numbers in one go, it is easier to use the PRE-update value rather than the POST-update value, i.e.
Reference for SQL Server UPDATE statement
插入带有初始种子的行。然后为插入分配足够大小的范围(显然在同一事务中调用它)
这将阻止其他并发尝试增加序列,直到第一个事务提交。
Insert a row with an initial seed. Then to allocate a range of sufficient size for your insert (call it in the same transaction obviously)
This will block other concurrent attempts to increment the sequence until the first transaction commits.