SQL Server INSERT,Scope_Identity()和物理写入光盘

时间:2010-03-15 10:20:34

标签: sql-server insert identity scope-identity

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

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顺序发生?

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

提前致谢。

6 个答案:

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

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

答案 1 :(得分:3)

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

如果你有两个独立的独立表,两者都用于查询和插入,两者都有一个单独的IDENTITY(1,1)字段,那么绝对无保证表示插入到一个表中然后进入第二个将以相同的顺序执行!

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

答案 2 :(得分:1)

回归写作:

  • 每当你做一些改变数据的事情时,就会在那一刻写入数据库LOGS,直到发生这种情况你才会得到交易确认。这就是ACID条件下的D(数据库理论)。
  • 脏的数据库页面“在后台”写入磁盘。如果太多是脏的,则触发检查点并将它们全部转出。

到目前为止写作部分。

你可能遇到的问题是,虽然单个语句是原子的,但繁忙的atabase可能有多个线程在运行。所以,基本上,语句之间发生了一个线程切换。一个线程得到了Id1,另一个得到了prioerity,id1,id2,然后是第一个id2。

此处没有特定内容;)多线程运行时的典型正常数据库行为。与写作本身无关。

基本上,介于两者之间 SET @MyID = Scope_Identity() 和下一个语句,另一个线程可以获得优先权;)

答案 3 :(得分:0)

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

答案 4 :(得分:0)

您应该能够通过使用SQL 2005功能OUTPUT子句来避免此问题。链接如下。

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

答案 5 :(得分:-1)

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

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

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

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

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