2010-11-18 12 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() 

私は私が連続したID値を持つテーブルのFooに挿入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レコードを挿入"ステートメントをシリアライズ可能なトランザクションでラップしようとしましたが、無駄に、私はまだインタリーブされたレコードを取得します。しかし、 "Insert 1000 records"を直列化可能トランザクションに入れ、挿入前にトランザクション内でSELECT MAX(ID)FROM Fooを呼び出すと、レコードが連続することが保証されます。このソリューションは同時インサートを防ぎますが、パフォーマンスの低下を招くことはありません。 –

+1

@John - ヒープ上のシリアライザブルは排他的なテーブルロックを取得するので、簡単なロックヒントで同じ効果を得ることができます。 'WITH(TABLOCKX)'これは並行性にも影響すると言っています。 –

+0

私の間違いは、私が実行していたテストでは、テーブルFooのIDカラムにPKを追加しました。 –

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は、合意されたが、テーブルの一生の間に、一切の保証は行われません - ギャップがあるかもしれない –

+0

申し訳ありませんが、私はマーティンのテスト@を通して走り、挿入の途中でレコードがテーブルに挿入されることを実際に示しています。私は、INSERTがアトミックであるというあなたの主張は依然として有効ですが、それは行がインターリーブ可能であるとは限りません。 –

+0

あなたの答えが連続した部分で間違っていたとしても、私はOUTPUT ... INTOを使用して自分の問題と@ KMの提案を解決しました。 –

3

を考えます複数の行のID値には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

0

てみてください、次の追加:このトランザクション内で、彼らは連続しています - -

option(maxdop 1)

+1

このオプションが設定されているマルチコアマシンで@Martinのテストを実行しましたか?私は結果を聞くことに興味があるだろう。 –

関連する問題