Sql server SQL Server插入、作用域\标识()和对光盘的物理写入

Sql server SQL Server插入、作用域\标识()和对光盘的物理写入,sql-server,insert,identity,scope-identity,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)中的每一个都有一个标识(1,1)列,我们称

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

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)中的每一个都有一个标识(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是一种表示“我不想自己麻烦地生成密钥,只要为我做就行了,如果需要,我会请求生成的值”

这里可能发生的情况是,两个线程同时插入行,但没有一个线程已经提交,因此您得到了以下场景:

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
解决问题有两种方法:

  • 删除第二个表中主键的标识
  • 使用
    SET IDENTITY\u INSERT ON
    可以为其提供密钥,同时保持标识设置
  • 然而,在这种情况下,我将使用方法nbr。1.方法nbr。将数据导入空表时通常使用2。您不希望数据库自动生成一个您以后想要自己使用的ID(因为它来自第一个表),因此您应该禁用第二个表的主键上的标识设置

    您可以尽量避免依赖该表的键,因为您有外键引用,您真的需要键值相同吗?

    重新编写:

    • 每当您执行更改数据的操作时,该操作都会立即写入数据库日志中,直到发生这种情况,您才会得到事务确认。这是酸性条件下的D(数据库理论)
    • 脏数据库页“在后台”写入磁盘。如果有太多的文件是脏的,则会触发一个检查点,并将它们全部转储出去
    到目前为止,到写作部分

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

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

    基本上,在 SET@MyID=Scope\u Identity()
    下一条语句,另一个线程可以获得优先级;)

    当然,您的上述场景是可能的,而且也很有可能

    如果您有两个独立的表,都用于查询和插入,都有一个单独的标识(1,1)字段,则绝对不能保证插入一个表和插入第二个表的顺序相同


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

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

    这是SQL Server中已知的错误

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

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

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


    啊哈,这里是知识库:

    您应该能够通过使用SQL 2005特性,即输出子句来避免这个问题。链接如下


    谢谢你的回答。。。这真的让我松了一口气:)然而,我实际上是将第一个表中的e ID作为外键插入另一个表中,但问题是我的ID是IDENTITY列,这导致了问题:)信息量很大。。。谢谢我完全同意你的看法。问题是,我使用的系统具有非常高的传输次数(每天数百万次),而该系统在很大程度上依赖标识列来标识行并将表链接到另一行。这是我无法改变的,我不得不忍受。。。我只是想确保我生活在最安全的一面,或者风险较小的一面:)我不久前读到过这个bug;不过,我不确定这是否与我的问题有关。在我的例子中,ID是在中正确生成的