2010-11-18 8 views
5

Rozważmy następujący SQL:Do włożonej płyty zawsze otrzymują Zwarty Tożsamość Wartości

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() 

Chciałabym wiedzieć, czy mogę zależeć od 1000 wierszy, które włożyłem w tabeli Foo konieczności ciągłych wartości tożsamości. W słowie porządkowym, jeśli ten blok SQL tworzy @LastID z 2000, czy mogę na pewno wiedzieć, że ID pierwszego wstawionego rekordu to 1001? Jestem głównie ciekawy wielu instrukcji wstawiających rekordy do tabeli Foo jednocześnie.

Wiem, że mogę dodać transakcję do serializacji wokół mojego oświadczenia wstawiania, aby zapewnić zachowanie, które chcę, ale czy naprawdę muszę? Obawiam się, że wprowadzenie transakcji serializowanej obniży wydajność, ale jeśli SQL Server nie pozwoli na wstawianie innych instrukcji do tabeli Foo, gdy ta instrukcja jest uruchomiona, nie muszę się tym martwić.

Odpowiedz

7

Nie zgadzam się z zaakceptowaną odpowiedzią. Można to łatwo sprawdzić i obalić, wykonując następujące czynności.

konfiguracji

USE tempdb 

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

Połączenie 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 

połączenie 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

Bardzo interesujące znalezienie! Doceniam, że składasz ten test. Próbowałem nawet owinąć instrukcję "Wstaw 1000 rekordów" w transakcję serializowalną, ale bez skutku, wciąż dostaję przeplatane rekordy. ALE, jeśli umieściłem "Wstaw 1000 rekordów" w transakcję nadającą się do szeregowania ORAZ zadzwoń do SELECT MAX (ID) OD Foo w transakcji przed moją wstawką, mogę wtedy zagwarantować, że moje rekordy będą ciągłe. To rozwiązanie uniemożliwia równoczesne wstawianie i nie jestem skłonny do przyjęcia trafienia wydajności, które mogłoby spowodować. –

+1

@John - serializowanie na stertę po prostu zdobędzie wyłączną blokadę stołu, więc myślę, że można osiągnąć ten sam efekt dzięki prostej podpowiedzi blokującej. 'WITH (TABLOCKX)' Tak jak mówisz, wpłynie to na współbieżność. –

+0

Mój błąd, w teście, w którym działałem, dodałem PK do kolumny ID na stole Foo, ponieważ to bardziej odzwierciedlało moją sytuację. –

6

Tak, będą przylegać, ponieważ INSERT jest atomowy: całkowite powodzenie lub pełne wycofanie. Jest również wykonywane jako pojedyncze jednostki pracy: nie dostaniesz żadnego „przeplot” z innymi procesami

jednak (! Lub umieścić swój umysł w spoczynku), należy rozważyć 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 zgodził - wewnątrz tej transakcji, będą one graniczyć - ale w życiu stołu, nie ma gwarancji w ogóle są - nie może być luki –

+0

Niestety, ale wpadłem przez @ testu Marcina i rzeczywiście pokazuje, że rekordy mogą być przeplatane do tabeli w środku wkładki. Jestem pewien, że twoje twierdzenie, że INSERT jest atomowe, jest nadal poprawne, ale wydaje się, że nie oznacza to, że wiersze mogą być przeplatane. –

+0

Jak się okazało, mimo że odpowiedź była błędna w części sąsiadującej, wciąż kończyłem używanie OUTPUT ... INTO, aby rozwiązać mój problem zgodnie z sugestią twojego i @ KM. –

3

Jeśli chcesz wartości Tożsamości dla wielu wierszy użyj WYJŚCIE:

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

masz teraz cały zestaw wartości w tabeli @NewIDs. Możesz dodać dowolne kolumny z tabeli Foo do tabeli @NewIDs i wstawić je również.

1

Nie jest dobrą praktyką dołączanie jakiegokolwiek znaczenia do wartości tożsamości. Powinieneś założyć, że są one niczym więcej niż liczbami całkowitymi gwarantowanymi jako unikalne w ramach twojego stołu.

+0

Jestem skłonny zgodzić się z tobą w teorii, ale w praktyce może to nie być problem. Osobiście szukałbym bardziej niezawodnego rozwiązania. – MikeAinOz

0

warto dodać następujące:

option(maxdop 1)

+1

Czy uruchomiłeś test @ Martina na maszynie wielordzeniowej z tym zestawem opcji? Byłbym zainteresowany, aby usłyszeć wyniki. –

Powiązane problemy