2009-10-08 56 views
1

我有一个使用事件号码(以及其他类型的号码)的应用程序。这些数字存储在名为“Number_Setup”的表中,该表包含计数器的当前值。确保来自sql server数据库的唯一号码

当应用程序生成一个新的事件时,它number_setup表并获得所需的数量计数器行(计数器可以重置每日,每周等,并存储为整数)。然后递增计数器并用新值更新行。

该应用程序是多用户(每次大约有100个用户,以及运行并抓取100个事件记录并为每个事件请求事件编号的sql作业)。事件表有一些重复的事件编号,它们不应该重复。

存储过程用于检索下一个计数器。

 
SELECT @Counter = counter, @ShareId=share_id, @Id=id 
FROM Number_Setup 
WHERE [email protected] 
AND Counter_Type='I' 

IF isnull(@ShareId,0) > 0 
BEGIN 
    -- use parent counter 
    SELECT @Counter = counter, @ID=id 
    FROM Number_Setup 
    WHERE [email protected] 
END 

SELECT @NewCounter = @Counter + 1 

UPDATE Number_Setup SET Counter = @NewCounter 
WHERE [email protected] 

现在我已经包围与事务块,但我不能完全肯定这将100%解决这个问题,我觉得还是有共享锁,因此计数器可以阅读反正。

也许我可以检查计数器尚未更新,在更新语句

 
UPDATE Number_Setup SET Counter = @NewCounter 
WHERE Counter = @Counter 
IF @@ERROR = 0 AND @@ROWCOUNT > 0 
    COMMIT TRANSACTION 
ELSE 
    ROLLBACK TRANSACTION 

我敢肯定,这是与金融应用的发票编号等
我不能把一个常见的问题代码中的逻辑,并在该级别使用锁定。 我也锁定在HOLDLOCK,但我不确定它的应用程序。它应该放在两个SELECT语句上吗?

我如何确保不会创建重复项?

+0

什么版本的Sql Server? – RBarryYoung 2009-10-08 22:58:19

+0

此刻,2000. – 2009-10-09 00:41:13

回答

4

关键是要做到计数器更新,并在单个原子操作阅读:

UPDATE Number_Setup SET Counter = Counter+1 
OUTPUT INSERTED.Counter 
WHERE [email protected]; 

这虽然不分配新的计数器@NewCounter,而是返回它设置成客户端的结果。如果非要给它分配,使用中间表变量输出新的计数器分为:

declare @NewCounter int; 
declare @tabCounter table (NewCounter int); 
UPDATE Number_Setup SET Counter = Counter+1 
OUTPUT INSERTED.Counter INTO @tabCounter (NewCounter) 
WHERE [email protected] 
SELECT @NewCounter = NewCounter FROM @tabCounter; 

这解决了使计数器增量原子的问题。您的程序中仍然存在其他争用条件,因为LinkTo_Id和share_id在第一次选择后仍然可以更新,因此您可以将错误的链接到计数器的计数器递增,但这不能仅从此代码示例中解决,因为它也取决于在实际更新shared_id和/或LinkTo_Id的代码上。

顺便说一句,你应该进入你的领域与一致的情况命名habbit。如果他们一致地命名然后你必须在T-SQL代码中使用完全匹配的情况。如果您部署在区分大小写的整理服务器上,并且您的脚本不匹配字段/表名称错误的确切大小写,那么您的脚本现在可以正常运行,因为您有一个不区分大小写的整理服务器。

+0

感谢您的反馈意见。模式不在我们的控制范围之内,我的例子已经组成了字段名称,并不是实际的sproc的直接副本:) – 2009-10-09 00:38:45

+0

非常有帮助,谢谢! – 2009-10-21 17:40:23

0

您是否尝试过使用GUID而不是自动增量作为唯一标识符?

0

如果你有能力修改你的工作获得多条记录,我会改变思路,让你的柜台是一个身份专栏。然后当你得到下一个记录时,你可以做一个插入并获得表格的@@标识。这将确保您获得最大的数量。您还需要执行dbccReseed来重置计数器,而不是仅在您要重置身份时更新表。唯一的问题是,你必须做100个左右的插入作为你的SQL作业的一部分来获得一组身份。这可能会带来很多开销,但使用身份列是获取唯一号码的一种保证。

+1

scope_identity()会比@@身份更好,以确保您恢复刚刚创建的身份。 – adrianbanks 2009-10-08 23:13:07

0

我可能会错过一些东西,但似乎您正在试图重塑已被大多数数据库解决的技术。

而不是从Number_Setup表中的'Counter'列中读取和更新,为什么不为您的计数器使用自动增量主键?您永远不会有主键的重复值。

+0

,因为计数器必须生成一个重置每日,每月或其他客户定义期的数字。不同客户的事件数字每天都会重置,有些事件从不重置。事件编号被放入事件表的一个字段中。该字段不是唯一的,只是索引。 现有的存储过程已经存在超过10年:) – 2009-10-09 00:40:38