2011-06-16 11 views
6

Ejecute la siguiente secuencia de comandos que crea y rellena una tabla en su base de datos de desarrollo.La identidad que se porta mal

SET NOCOUNT ON 
Drop Table dbo.Region 
GO 
CREATE TABLE dbo.Region(
    RegionId  int    IDENTITY(1,1), 
    RegionName varchar(100) NOT NULL 
) 
GO 

INSERT INTO dbo.Region (RegionName) 
VALUES ('Region One'), 
     ('Region Two'); 
GO 

SELECT * FROM dbo.Region 

El resultado de esto es lo que cabría esperar de un campo de identidad con buen comportamiento.

RegionId RegionName 
----------- ------------------ 
1   Region One 
2   Region Two 

Ahora forcemos un par de valores en la columna Identidad.

SET NOCOUNT ON 
Drop Table dbo.Region 
GO 
CREATE TABLE dbo.Region(
    RegionId  int    IDENTITY(1,1), 
    RegionName varchar(100) NOT NULL 
) 
GO 

SET IDENTITY_INSERT dbo.Region ON; 
INSERT INTO dbo.Region (RegionId, RegionName) 
VALUES (-9, 'Unknown'), 
     (-99, 'N/A'); 
SET IDENTITY_INSERT dbo.Region OFF; 

INSERT INTO dbo.Region (RegionName) 
VALUES ('Region One'), 
     ('Region Two'); 
GO 

SELECT * FROM dbo.Region 

La salida es

RegionId RegionName 
----------- ------------------ 
-9   Unknown 
-99   N/A 
2   Region One 
3   Region Two 

¿Dónde regionId = 1 ir?




Editar En la investigación adicional, SQL-Server no saltar nada si se intenta el mismo truco dos veces

SET NOCOUNT ON 
Drop Table dbo.Region 
GO 
CREATE TABLE dbo.Region(
    RegionId  int    IDENTITY(1,1), 
    RegionName varchar(100) NOT NULL 
) 
GO 

SET IDENTITY_INSERT dbo.Region ON; 
INSERT INTO dbo.Region (RegionId, RegionName) 
VALUES (-9, 'Unknown'), 
     (-99, 'N/A'); 
SET IDENTITY_INSERT dbo.Region OFF; 

INSERT INTO dbo.Region (RegionName) 
VALUES ('Region One'), 
     ('Region Two'); 
GO 
SET IDENTITY_INSERT dbo.Region ON; 
INSERT INTO dbo.Region (RegionId, RegionName) 
VALUES (-999, 'Known-Unknown'), 
     (-9999, 'Really N/A'); 
SET IDENTITY_INSERT dbo.Region OFF; 

INSERT INTO dbo.Region (RegionName) 
VALUES ('Region Four'), 
     ('Region Five'); 
GO 

SELECT * FROM dbo.Region 

La salida aquí es

RegionId RegionName 
----------- ------------------ 
-9   Unknown 
-99   N/A 
2   Region One 
3   Region Two 
-999  Known-Unknown 
-9999  Really N/A 
4   Region Four 
5   Region Five 

En el caso anterior, el 1 desapareció. ¡Aquí el 4 no desapareció!

¡Así que ahora esta es la identidad impredecible y faltante!

¿Por qué el regionId = 1 go falta, pero el regionId = 4 no van a faltar ?!

+1

¿Es esto para pura investigación o realmente necesitas el 1? Lo pregunto porque el consenso general es que si usa IDENTITY/AUTONUMBER está diciendo "realmente no me importa cuál es el valor * actual *, siempre que sea único". Es decir, si * te importa * cuál es el valor, pero no lo suficiente para asegurarte de que sea un valor específico, simplemente dejas que la base de datos decida. –

+0

Estoy bastante seguro de que entiendo qué es una clave sustituta y cómo se usa un campo de Identidad. No necesito _necesita el '1'. Sin embargo, como sin duda ve en mis dos ejemplos, este se fue sin permiso. Quiero saber a dónde fue y por qué. –

+0

Y estoy de acuerdo, ¡esa es una pregunta muy buena e interesante! –

Respuesta

6

IDENTITY(1,1) aplica a la primera fila de la tabla

Puesto que ya tiene dos filas insertadas la semilla ya no se aplica

El siguiente algoritmo de identidad es la adición de una al inicio de semillas cuando detecta que hay los registros existentes en la tabla desde 1 pueden haber sido ya utilizados.

+0

Pero Jimbo, no toqué la semilla, ¡inserté mi propia identidad! –

+2

la semilla de 1 solo se aplica al PRIMER registro, agregando sus propios registros antes de que se pueda usar la semilla que usted afectó indirectamente - el siguiente algoritmo de identidad sabe que hay registros existentes por lo que puede ser inútil comenzar con la primera semilla , correcta o incorrectamente, comienza a buscar semilla + 1 para la siguiente identidad disponible. – Jimbo

+1

@Raj Más: incluso puede eliminar sus primeras filas * antes * insertando otras, y aún así terminaría con el valor de la columna de identidad de la siguiente fila en 2. Quiero decir, insertó filas que nunca usaron la identidad 1, y luego las eliminó , y eso aún le impediría obtener la identidad 1 automáticamente. –

3

Lo he notado también, el primer valor de identidad es de alguna manera especial. Si realiza una transacción en la tabla, se elimina la primera identidad. Esto no es cierto para las siguientes identidades.

La razón parece ser que el current identity comienza con un valor NULL:

create table YourTable (id int identity) 
dbcc checkident(YourTable) 
--> 
Checking identity information: current identity value 'NULL', 
current column value 'NULL'. 

Cuando el current identity es NULL, el siguiente número que haya de atribuirse a cabo es 1.Sin embargo, cualquier transacción, incluso uno que no consume una nueva identidad, hace que el current identity para cambiar de NULL a 1:

set identity_insert YourTable on; 
insert YourTable (id) values (-1) 
set identity_insert YourTable off; 
dbcc checkident(YourTable) 
--> 
Checking identity information: current identity value '1', 
current column value '1'. 

Con la current identity de 1, el siguiente número será 2:

insert YourTable default values 
select * from YourTable 
--> 
2 

Por lo tanto, la primera transacción en una tabla nueva tiene un efecto especial en el current identity. No tiene que ser una nueva tabla, sucede después de truncate table también.

1

De SQL Server Books Online

IDENTITY [ (seed , increment) ] 

semilla

es el valor que se utiliza para la primera fila cargada en la tabla.

incremento

Es el valor incremental que se agrega al valor de identidad de la fila anterior que se ha cargado.

, ya que utiliza IDENTITY_INSERT para cargar la primera fila en la tabla de la seed en ese caso no se requería. Para IDENTITY_INSERT también está documentado que

Si el valor introducido es mayor que el valor de identidad actual para la tabla, SQL Server utiliza automáticamente el nuevo valor insertado como el valor de identidad actual .

Se deja sin especificar lo que debe suceder en el caso descrito en su pregunta, pero está claro que sólo se establece el valor de identidad actual a MAX(seed,identity_inserted) desde el siguiente

CREATE TABLE dbo.Region(
    RegionId  int    IDENTITY(1,1), 
    RegionName varchar(100) NOT NULL 
) 

SELECT increment_value, 
     last_value as last_value_new_table 
FROM sys.identity_columns 
WHERE name ='RegionId' AND object_id=object_id('dbo.Region') 

SET IDENTITY_INSERT dbo.Region ON; 
INSERT INTO dbo.Region (RegionId, RegionName) 
VALUES (-9, '-9') 
SET IDENTITY_INSERT dbo.Region OFF; 

SELECT increment_value, 
     last_value as last_value_after_insert 
FROM sys.identity_columns 
WHERE name ='RegionId' AND object_id=object_id('dbo.Region') 

DROP TABLE dbo.Region 

devoluciones

increment_value    last_value_new_table 
------------------------------ ------------------------------ 
1        NULL 

increment_value    last_value_after_insert 
------------------------------ ------------------------------ 
1        1 
Cuestiones relacionadas