在SQL Server中获取完全顺序int值的最佳方法

时间:2011-04-04 11:03:16

标签: sql-server tsql triggers

我的业务要求是InvoiceNumber表中的Invoices字段完全是连续的 - 没有间隙或审计员可能会认为我们的会计师有些可疑!

我的第一个想法是简单地使用主键(标识),但如果事务被回滚,则序列中会出现间隙。

所以我的第二个想法是使用一个触发器,在插入点,它会在表中查找最高InvoiceNumber值,向其中添加1,并将其用作InvoiceNumber新行。易于实施。

近同步插入是否存在潜在问题?例如,同时运行触发器的两个几乎同时插入可能获得相同的“当前最高InvoiceNumber”值,因此插入具有相同InvoiceNumber的行?

我可能还有其他问题吗?另一种方法会更好吗?

3 个答案:

答案 0 :(得分:0)

创建一个保存“计数器”轨迹的表格。 对于您的发票,您可以向该表添加一些记录,以跟踪必须使用的下一个整数 创建发票时,您应该使用该值并增加它。当您的事务回滚时,该计数器的更新也将被回滚。 (确保您锁定该表,以确保没有其他进程可以使用相同的值。)

这比查看发票表中使用的最高当前计数器要可靠得多。

答案 1 :(得分:0)

CREATE TABLE dbo.Sequence(
 val int 
 )

插入包含初始种子的行。然后为插入分配一个足够大小的范围(显然在同一个事务中调用它)

CREATE PROC dbo.GetSequence
@val AS int OUTPUT,
@n as int =1
AS
UPDATE dbo.Sequence 
SET @val = val = val + @n;

SET @val = @val - @n + 1; 

这将阻止其他并发尝试增加序列,直到第一个事务提交。

答案 2 :(得分:0)

如果从表中删除数据,您可能仍会遇到空白。但是如果数据只进入而不进入,那么通过在外部序列表上正确使用事务,应该可以很好地完成这项工作。不要使用MAX()+ 1因为它可能有时间问题,或者你可能需要锁定更多的表(页面/表格)而不是所需的。

拥有一个只有一个记录和列的顺序表。以原子方式从表中检索数字,将检索和使用包装在单个事务中。

begin tran
declare @next int
update seqn_for_invoice set @next=next=next+1
insert invoice (invoicenumber,...) value (@next, ....)
commit

UPDATE语句是原子的,不能被中断,双重赋值使@next的值成为原子。它相当于使用SQL Server 2005+中的OUTPUT子句来返回更新的值。如果您需要一次性使用一系列数字,则更容易使用PRE更新值而不是POST更新值,即

begin tran
declare @next int
update seqn_for_invoice set @next=next, next=next+3   -- 3 in one go
insert invoice (invoicenumber,...) value (@next, ....)
insert invoice (invoicenumber,...) value (@next+1, ....)
insert invoice (invoicenumber,...) value (@next+2, ....)
commit

SQL Server UPDATE statement

的参考
  

SET @variable = column = expression将变量设置为与列相同的值。这与SET @variable = column,column = expression不同,后者将变量设置为列的更新前值。