2010-11-18 31 views
5

請看下面的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() 

我想知道如果我可以依靠,我插入具有連續的標識值表美孚的1000行。換句話說,如果這個SQL塊生成一個@LastID爲2000,我可以肯定地知道我插入的第一條記錄的ID是1001嗎?我主要好奇多個語句同時向Foo表中插入記錄。

我知道我可以在我的insert語句周圍添加一個可序列化的事務來確保我想要的行爲,但是我真的需要嗎?我擔心引入一個可序列化的事務會降低性能,但是如果SQL Server在這個語句運行時不允許其他語句插入表Foo中,那麼我不必擔心它。

回答

7

我不同意接受的答案。這可以通過運行以下內容輕鬆進行測試和反駁。

設置

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 
+0

非常有趣的發現!我很感謝你把這個測試放在一起。我甚至試圖在可序列化事務中包裝「插入1000條記錄」語句,但無濟於事,我仍然得到了交錯記錄。但是,如果我將「插入1000條記錄」放入一個可序列化的事務中並在插入之前在事務中調用SELECT MAX(ID)FROM Foo,那麼我可以保證我的記錄是連續的。雖然這個解決方案可以防止同時插入,但我不願意承擔會導致的性能下降。 – 2010-11-19 21:08:17

+1

@John - 在堆上可序列化只會獲得一個獨佔表鎖,我認爲你可以通過一個簡單的鎖定提示實現相同的效果。 WITH(TABLOCKX)'正如你所說,這會影響併發性。 – 2010-11-19 21:11:28

+0

我的錯誤,在我運行的測試中,我在表Foo的ID列中添加了一個PK,因爲這更能代表我的情況。 – 2010-11-19 21:12:53

6

是的,它們將是連續的,因爲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 
+0

+1同意 - 這個交易裏面,他們將是連續的 - 但在表的生存,不能保證任何由 - 可能有差距 – 2010-11-18 21:32:43

+0

很抱歉,但我通過@馬丁的測試運行,並它確實表明記錄可以在插入的中間插入到表格中。我確信你的INSERT是原子的斷言仍然有效,但它似乎並不意味着行可以交錯。 – 2010-11-19 21:04:31

+0

事實證明,即使您的答案在連續部分出現錯誤,我仍然最終使用OUTPUT ... INTO來解決我的問題,根據您的和@ KM的建議。 – 2010-11-19 21:10:19

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表中,並插入這些列。

1

將任何形式的含義都附加到身份值並不是一種好習慣。你應該假設它們只不過是在你的表的範圍內保證唯一的整數。

+0

我傾向於在理論上贊同你,但實際上這可能不成問題。我個人會尋找更強大的解決方案。 – MikeAinOz 2010-11-18 21:37:58

0

嘗試增加以下內容:

option(maxdop 1)

+1

您是否在使用此選項集的多核計算機上運行@ Martin測試?我有興趣聽到結果。 – 2011-03-10 13:12:18