6

我有一个存储过程,除其他外,它会在循环内的不同表中插入一些内容。请参阅下面的示例以获得更清晰的理解:

INSERT INTO T1 VALUES ('something')

SET @MyID = Scope_Identity()

... some stuff go here

INSERT INTO T2 VALUES (@MyID, 'something else')

... The rest of the procedure

这两个表(T1 和 T2)各有一个 IDENTITY(1, 1) 列,我们称它们为 ID1 和 ID2;但是,在我们的生产数据库(非常繁忙的数据库)中运行该过程并在每个表中有超过 6250 条记录后,我注意到 ID1 与 ID2 不匹配的事件!尽管通常对于插入到 T1 中的每条记录,在 T2 中都插入了记录,并且两者中的标识列都一致地递增。

“错误”的记录是这样的:

ID1     Col1
----    ---------
4709    data-4709
4710    data-4710

ID2     ID1     Col1
----    ----    ---------
4709    4710    data-4710
4710    4709    data-4709

注意第二个表中的“倒置”ID1。

对SQL Server底层操作了解不多,我提出了以下“理论”,也许有人可以纠正我。

我认为是因为循环比物理写入表更快,和/或可能其他一些事情延迟了写入过程,所以记录被缓冲了。到了写它们的时候,它们的写法没有特别的顺序。

如果没有,那是否可能,如何解释上述情况?

如果是,那么我还有另一个问题要提出。如果第一次插入(来自上面的代码)被延迟了怎么办?这是否意味着我不会获得正确的 IDENTITY 插入第二个表?如果这个答案也是肯定的,我该怎么做才能确保两个表中的插入会以正确的 IDENTITY 顺序发生?

我感谢任何有助于我理解这一点的评论和信息。

提前致谢。

4

6 回答 6

3

您无法依靠 IDENTITY 为您的第二张表解决此问题。如果您关心为该行生成的主键值,您应该生成它自己。

IDENTITY 是一种说法,“我不想自己生成密钥的麻烦,只需为我做,我会在需要时询问生成的值”。

这里可能发生的是两个线程同时插入行,它们都还没有提交,所以你会得到这个场景:

Thread 1                      Thread 2
get id for table 1 = 4709
                              get id for table 1 = 4710
insert row for table 1
                              insert row for table 1
                              get id for table 2 = 4709
get id for table 2 = 4710
                              insert row for table 2
insert row for table 1

您有两种方法可以解决您的问题:

  1. 删除第二个表中主键的 IDENTITY
  2. 用于SET IDENTITY_INSERT ON允许您为其提供密钥,同时保留 IDENTITY 设置

但是,在这种情况下,我会使用方法 nbr。1.方法nbr。2 通常在将数据导入空表时使用。您不希望数据库自动生成您以后想要自己使用的 ID 的风险(因为它来自第一个表),因此您应该禁用第二个表的主键上的 IDENTITY 设置。

或者您可以尝试完全避免依赖该表的键,因为您有一个外键引用,您真的需要键值相同吗?

于 2010-03-15T10:26:31.377 回答
3

当然,您的上述情况是可能的 - 也很有可能。

如果您有两个单独的、独立的表,都用于查询和插入,都具有单独的 IDENTITY(1,1) 字段,则绝对不能保证插入一个表然后再插入第二个表将在同一个表中执行命令!

如果确实需要在两者之间建立链接,请将第一个表的 ID 作为外键插入到第二个表中。您不能依赖从 IDENTITY 生成的 ID 在两个表中相同!

于 2010-03-15T10:28:34.597 回答
1

重温写作:

  • 每当你做一些改变数据的事情时,这会被写入数据库 LOGS 中,并且在这发生之前你不会得到事务确认。那是ACID条件下的D(数据库理论)。
  • 脏数据库页面“在后台”写入磁盘。如果脏的太多,就会触发一个检查点并将它们全部丢弃。

至此,写作部分。

您可能会遇到一个简单的事实,即虽然单个语句是原子的,但一个繁忙的数据库可能有多个线程在其上运行。因此,基本上,语句之间发生了线程切换。一个线程得到 Id1,另一个线程得到优先级,id1,id2,然后是第一个 id2。

这里没有什么特别的;)当多个线程同时运行时典型的正常数据库行为。与写作本身无关。

基本上,在 SET @MyID = Scope_Identity() 和下一条语句之间,另一个线程可以获得优先级;)

于 2010-03-15T10:26:36.183 回答
0

不要依赖于业务/应用程序逻辑的标识列的实际值,您只能假设它们是唯一的!

于 2010-03-15T13:23:59.523 回答
0

您应该能够通过使用 SQL 2005 的特性 OUTPUT 子句来避免这个问题。下方链接。

http://msdn.microsoft.com/en-us/library/ms177564.aspx

于 2010-03-15T17:19:04.543 回答
-1

这是 SQL Server 中的一个已知错误。

问题是当它生成查询计划时,并行化会导致范围标识不正确。

将该部分移动到它自己的过程中,因此传入参数并返回范围标识 - 现在它应该是正确的。

如果我没记错的话,这只会出现在大约有一百万行或更多行的表上。

啊哈,这是知识库: http: //support.microsoft.com/default.aspx ?scid=kb;en-us;2019779&sd=rss&spid=2855

于 2010-03-15T13:44:05.680 回答