【问题标题】:Do Inserted Records Always Receive Contiguous Identity Values插入的记录是否总是接收连续的标识值
【发布时间】:2010-11-18 20:22:47
【问题描述】:

考虑以下 SQL:

CREATE TABLE Foo
(
    ID int IDENTITY(1,1),
    Data nvarchar(max)
)

INSERT INTO Foo (Data)
SELECT TOP 1000 Data
FROM SomeOtherTable
WHERE SomeColumn = @SomeParameter

DECLARE @LastID int
SET @LastID = SCOPE_IDENTITY()

我想知道我是否可以依赖插入到表 Foo 中的 1000 行具有连续的标识值。换句话说,如果这个 SQL 块产生的 @LastID 为 2000,我是否可以确定我插入的第一条记录的 ID 是 1001?我主要对同时将记录插入表 Foo 的多个语句感到好奇。

我知道我可以在我的插入语句周围添加一个可序列化的事务来确保我想要的行为,但我真的需要吗?我担心引入可序列化事务会降低性能,但如果 SQL Server 在此语句运行时不允许其他语句插入到表 Foo 中,那么我不必担心。

【问题讨论】:

    标签: sql-server sql-server-2005


    【解决方案1】:

    我不同意接受的答案。这可以通过运行以下命令轻松测试和反驳。

    设置

    USE tempdb
    
    CREATE TABLE Foo
    (
        ID int IDENTITY(1,1),
        Data nvarchar(max)
    )
    

    连接 1

    USE tempdb
    
    SET NOCOUNT ON
    WHILE NOT EXISTS(SELECT * FROM master..sysprocesses WHERE context_info = CAST('stop' AS VARBINARY(128) ))
     BEGIN
     INSERT INTO Foo (Data)
     VALUES ('blah')
     END
    

    连接 2

    USE tempdb
    
    SET NOCOUNT ON
    SET CONTEXT_INFO 0x
    
    DECLARE @Output TABLE(ID INT)
    
    WHILE 1 = 1
    BEGIN
        /*Clear out table variable from previous loop*/
        DELETE FROM  @Output
    
        /*Insert 1000 records*/
        INSERT INTO Foo (Data)
        OUTPUT inserted.ID INTO @Output
        SELECT TOP 1000 NEWID()
        FROM sys.all_columns
    
        IF EXISTS(SELECT * FROM @Output HAVING MAX(ID) - MIN(ID) <> 999 )
            BEGIN
            /*Set Context Info so other connection inserting 
              a single record in a loop terminates itself*/
            DECLARE @stop VARBINARY(128) 
            SET @stop = CAST('stop' AS VARBINARY(128))
            SET CONTEXT_INFO @stop
    
            /*Return results for inspection*/
            SELECT ID, DENSE_RANK() OVER (ORDER BY Grp) AS ContigSection
            FROM 
              (SELECT ID, ID - ROW_NUMBER() OVER (ORDER BY [ID]) AS Grp
               FROM @Output) O
            ORDER BY ID
    
            RETURN
            END
    END
    

    【讨论】:

    • 非常有趣的发现!我很感激你把这个测试放在一起。我什至尝试将“插入 1000 条记录”语句包装在可序列化的事务中,但无济于事,我仍然得到交错的记录​​。但是,如果我将“插入 1000 条记录”放入可序列化的事务中并在插入之前在事务中调用 SELECT MAX(ID) FROM Foo,那么我可以保证我的记录是连续的。但是,此解决方案可以防止同时插入,而且我不愿意承受可能导致的性能损失。
    • @John - 我认为堆上的可序列化只会获得一个独占表锁,因此您可以通过简单的锁定提示实现相同的效果。 WITH (TABLOCKX) 正如你所说,这会影响并发性。
    • 我的错误,在我运行的测试中,我在 Foo 表的 ID 列中添加了一个 PK,因为这更接近于我的情况。
    • 好电话。经过一些研究sqlblog.com/blogs/paul_white/archive/2010/10/19/… 这解释了我几年前在研讨会上听到但无法复制或找到参考的东西
    • select max(id) - MIN(id), COUNT(*) from foo 表有一系列无缝的 id,来自不同的会话。连接 1 不使用原子插入或事务。那你证明什么?
    【解决方案2】:

    是的,它们将是连续的,因为 INSERT 是原子的:完全成功或完全回滚。它也作为单个工作单元执行:您不会与其他进程发生任何“交错”

    但是(或者让您放心!),请考虑OUTPUT clause

    DECLARE @KeyStore TABLE (ID int NOT NULL)
    
    INSERT INTO Foo (Data)
    OUTPUT INSERTED.ID INTO @KeyStore (ID) --this line
    SELECT TOP 1000 Data
    FROM SomeOtherTable
    WHERE SomeColumn = @SomeParameter
    

    【讨论】:

    • +1 同意 - 在此事务中,它们将是连续的 - 但在表的生命周期内,不做任何保证 - 可能存在间隙
    • 抱歉,我通过了@Martin 的测试,它确实表明记录可以在插入中间交错到表中。我确信您关于 INSERT 是原子的断言仍然有效,但这似乎并不意味着行可以交错。
    • 事实证明,即使您的回答在连续部分有误,我仍然最终使用 OUTPUT ... INTO 根据您和@KM 的建议解决我的问题。
    • Martin 的测试不使用原子插入。他在一个while循环中运行了一些独立的插入。
    • @bernd_k - 请注意,Martin 正在对 1000 行的原子插入执行 OUTPUT INSERTED 并检查非连续值。
    【解决方案3】:

    如果您想要多行的标识值,请使用 OUTPUT:

    DECLARE @NewIDs table (PKColumn int)
    INSERT INTO Foo (Data)
        OUTPUT INSERTED.PKColumn
        INTO @NewIDs
    SELECT TOP 1000 Data
    FROM SomeOtherTable
    WHERE SomeColumn = @SomeParameter
    

    您现在拥有@NewIDs 表中的整个值集。您可以将 Foo 表中的任何列添加到 @NewIDs 表中并插入这些列。

    【讨论】:

      【解决方案4】:

      将任何类型的含义附加到身份值上都不是好习惯。您应该假设它们只不过是保证在您的表范围内唯一的整数。

      【讨论】:

      • 理论上我倾向于同意你的观点,但实际上这可能不是问题。我个人会寻找更强大的解决方案。
      【解决方案5】:

      尝试添加以下内容:

      option(maxdop 1)

      【讨论】:

      • 您是否在设置了此选项的多核机器上运行@Martin 的测试?我很想知道结果。
      猜你喜欢
      • 2013-01-20
      • 2015-10-04
      • 1970-01-01
      • 2011-07-14
      • 1970-01-01
      • 2020-07-14
      • 2017-12-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多