2010-11-18 9 views
5

Consideremos el siguiente código SQL:Do insertados Records Recibe Siempre Identidad Contigua Valores

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

me gustaría saber si puedo depender de las 1000 filas que se inserta en la tabla de Foo tener valores de identidad contiguos. En otras palabras, si este bloque SQL produce un @LastID de 2000, ¿puedo saber con certeza que el ID del primer registro que inserté fue 1001? Principalmente me interesan las declaraciones múltiples que insertan registros en la tabla Foo al mismo tiempo.

Sé que podría agregar una transacción serializable alrededor de mi declaración de inserción para garantizar el comportamiento que quiero, ¿pero realmente lo necesito? Me preocupa que la introducción de una transacción serializable deteriorará el rendimiento, pero si SQL Server no permite que otras instrucciones se inserten en la tabla Foo mientras se ejecuta esta declaración, entonces no tengo que preocuparme por ello.

Respuesta

7

No estoy de acuerdo con la respuesta aceptada. Esto se puede probar y refutar fácilmente ejecutando lo siguiente.

Configuración

USE tempdb 

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

conexión 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 

Conexión 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

¡Muy interesante descubrimiento! Te agradezco que hayas armado esta prueba. Incluso traté de ajustar la declaración "Insertar 1000 registros" en una transacción serializable, pero fue en vano, aún recibo registros intercalados. PERO, si coloco "Insertar 1000 registros" en una transacción serializable Y llamo SELECCIONAR MAX (ID) DE Foo dentro de la transacción antes de mi inserción, puedo garantizar que mis registros serán contiguos. Esta solución evita insertos simultáneos, sin embargo, y no estoy dispuesto a tomar el golpe de rendimiento que podría causar. –

+1

@John - serializable en un montón simplemente adquirirá un exclusivo bloqueo de mesa, creo que así podría obtener el mismo efecto con una simple pista de bloqueo. 'WITH (TABLOCKX)' Como dices, esto tendrá un impacto en la concurrencia. –

+0

mi error, en la prueba que estaba ejecutando agregué un PK a la columna ID en la tabla Foo, ya que eso representaba más de cerca mi situación. –

6

Sí, estarán contiguos porque INSERT es atómico: éxito completo o reversión completa. También se lleva a cabo como una sola unidad de trabajo: no vas a encontrar ninguna "intercalado" con otros procesos

Sin embargo (! O para poner su mente en reposo), consideran el 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 acordaron - dentro de esta transacción, que será contigua - pero en el curso de la vida de la mesa, no hay garantía alguna se hacen - que puede haber lagunas –

+0

Lo siento, pero corrió a través de @ test de Martin y de hecho, muestra que los registros se pueden intercalar en la tabla en el medio de un inserto. Estoy seguro de que su afirmación de que INSERT es atómico sigue siendo válida, pero parece que eso no significa que las filas se puedan intercalar. –

+0

Resulta que, aunque tu respuesta se equivocó en la parte contigua, aún terminé usando OUTPUT ... INTO para resolver mi problema según tu sugerencia y @ KM. –

3

Si desea los valores de identidad de varias filas utilizar SALIDA:

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

ahora tiene todo el conjunto de valores de la tabla @NewIDs. Puede agregar cualquier columna de la tabla Foo en la tabla @NewIDs e insertar esas columnas también.

1

No es una buena práctica adjuntar ningún tipo de significado a los valores de identidad. Debe suponer que no son más que enteros garantizados como únicos dentro del alcance de su tabla.

+0

Me inclino a estar de acuerdo con usted en teoría, pero en la práctica esto puede no ser un problema. Personalmente, buscaría una solución más robusta. – MikeAinOz

0

Trate de añadir el siguiente:

option(maxdop 1)

+1

¿Ha ejecutado la prueba de @ Martin en una máquina multi-core con esta opción configurada? Me interesaría escuchar los resultados. –

Cuestiones relacionadas