PRIMERO
Es probable que no necesita las tres columnas: old_id
, external_id
, new_id
. La new_id
columna, siendo un IDENTITY
, tendrá un nuevo valor generado para cada fila, incluso cuando inserte en external_id
. Pero, entre old_id
y external_id
, esos son más o menos mutuamente excluyentes: o ya hay un old_id
valor o esa columna, en la concepción actual, solo lo será NULL
si se usa external_id
o new_id
. Como no va a agregar una nueva identificación "externa" a una fila que ya existe (es decir, una que tenga un old_id
valor), y no habrá valores nuevos old_id
, entonces puede haber una columna que se utilice para ambos propósitos
Entonces, deshágase de la external_id
columna y cambie el nombre old_id
para que sea algo así old_or_external_id
o lo que sea. Esto no debería requerir ningún cambio real en nada, pero reduce algunas de las complicaciones. A lo sumo, es posible que deba llamar a la columna external_id
, incluso si contiene valores "antiguos", si el código de la aplicación ya está escrito para insertarlo external_id
.
Eso reduce la nueva estructura para ser justa:
PkId AS AS COALESCE(old_or_external_id, new_id, -1) PERSISTED NOT NULL,
old_or_external_id INT NULL, -- values from existing record OR passed in from app
new_id INT IDENTITY(2000000, 1) NOT NULL
Ahora solo ha agregado 8 bytes por fila en lugar de 12 bytes (suponiendo que no esté utilizando la SPARSE
opción o la compresión de datos). Y no necesitaba cambiar ningún código, T-SQL o código de aplicación.
SEGUNDO
Continuando por este camino de simplificación, veamos lo que nos queda:
- La
old_or_external_id
columna ya tiene valores, o se le dará un nuevo valor desde la aplicación, o se dejará como NULL
.
- El
new_id
siempre tendrá un nuevo valor generado, pero ese valor sólo se utilizará si el old_or_external_id
columna es NULL
.
Nunca hay un momento en que necesite valores en ambos old_or_external_id
y new_id
. Sí, habrá momentos en que ambas columnas tienen valores debido a new_id
ser un IDENTITY
, pero esos new_id
valores se ignoran. De nuevo, estos dos campos son mutuamente excluyentes. ¿Y ahora qué?
Ahora podemos ver por qué necesitábamos el external_id
en primer lugar. Teniendo en cuenta que es posible insertar en una IDENTITY
columna usando SET IDENTITY_INSERT {table_name} ON;
, podría salirse con la suya sin hacer ningún cambio de esquema y solo modificar el código de su aplicación para envolver las INSERT
declaraciones / operaciones en SET IDENTITY_INSERT {table_name} ON;
y SET IDENTITY_INSERT {table_name} OFF;
declaraciones. Luego, debe determinar a qué rango inicial restablecer la IDENTITY
columna (para los valores recién generados), ya que deberá estar muy por encima de los valores que el código de la aplicación insertará, ya que la inserción de un valor más alto hará que el siguiente valor generado automáticamente ser mayor que el valor MAX actual. Pero siempre puede insertar un valor que esté por debajo del valor IDENT_CURRENT .
La combinación de las columnas old_or_external_id
y new_id
tampoco aumenta las posibilidades de encontrarse con una situación de valor superpuesto entre los valores generados automáticamente y los valores generados por la aplicación, ya que la intención de tener las columnas 2, o incluso 3, es combinarlas en un valor de clave primaria, y esos son siempre valores únicos.
En este enfoque, solo necesita:
Deje las tablas como:
PkId INT IDENTITY(1,1) PRIMARY KEY
Esto agrega 0 bytes a cada fila, en lugar de 8, o incluso 12.
- Determine el rango inicial para los valores generados por la aplicación. Estos serán mayores que el valor MAX actual en cada tabla, pero menores que lo que se convertirá en el valor mínimo para los valores autogenerados.
- Determine en qué valor debe comenzar el rango autogenerado. Debe haber mucho espacio entre el valor MAX actual y mucho espacio para crecer, sabiendo que en el límite superior está un poco más de 2.14 mil millones. Luego puede establecer este nuevo valor mínimo inicial a través de DBCC CHECKIDENT .
- Envuelva el código de la aplicación INSERT en
SET IDENTITY_INSERT {table_name} ON;
y SET IDENTITY_INSERT {table_name} OFF;
declaraciones.
SEGUNDO, Parte B
Una variación en el enfoque anotado directamente arriba sería hacer que los valores de inserción del código de la aplicación comiencen con -1 y desciendan desde allí. Esto deja a los IDENTITY
valores por ser los únicos que van hacia arriba . El beneficio aquí es que no solo no complica el esquema, sino que tampoco tiene que preocuparse por encontrarse con ID superpuestos (si los valores generados por la aplicación se encuentran en el nuevo rango generado automáticamente). Esta es solo una opción si aún no está utilizando valores de ID negativos (y parece bastante raro que las personas usen valores negativos en columnas generadas automáticamente, por lo que esta debería ser una posibilidad en la mayoría de las situaciones).
En este enfoque, solo necesita:
Deje las tablas como:
PkId INT IDENTITY(1,1) PRIMARY KEY
Esto agrega 0 bytes a cada fila, en lugar de 8, o incluso 12.
- El rango inicial para los valores generados por la aplicación será
-1
.
- Envuelva el código de la aplicación INSERT en
SET IDENTITY_INSERT {table_name} ON;
y SET IDENTITY_INSERT {table_name} OFF;
declaraciones.
Aquí aún debe hacer lo siguiente IDENTITY_INSERT
, pero: no agrega ninguna columna nueva, no necesita "volver a colocar" ninguna IDENTITY
columna y no tiene riesgo futuro de superposiciones.
SEGUNDO, Parte 3
Una última variación de este enfoque sería posiblemente intercambiar las IDENTITY
columnas y, en su lugar, usar Secuencias . La razón para adoptar este enfoque es poder tener los valores de inserción del código de la aplicación que son: positivo, superior al rango generado automáticamente (no inferior) y sin necesidad de hacerlo SET IDENTITY_INSERT ON / OFF
.
En este enfoque, solo necesita:
- Crear secuencias usando CREAR SECUENCIA
Copie la IDENTITY
columna a una nueva columna que no tenga la IDENTITY
propiedad, pero que tenga una DEFAULT
Restricción usando la función NEXT VALUE FOR :
PkId INT PRIMARY KEY CONSTRAINT [DF_TableName_NextID] DEFAULT (NEXT VALUE FOR...)
Esto agrega 0 bytes a cada fila, en lugar de 8, o incluso 12.
- El rango inicial para los valores generados por la aplicación estará muy por encima de lo que cree que se acercarán los valores generados automáticamente.
- Envuelva el código de la aplicación INSERT en
SET IDENTITY_INSERT {table_name} ON;
y SET IDENTITY_INSERT {table_name} OFF;
declaraciones.
Sin embargo , debido a la exigencia de que el código, ya sea con SCOPE_IDENTITY()
o @@IDENTITY
aún funciona correctamente, el cambio a secuencias no es actualmente una opción ya que parece que no existe un equivalente de esas funciones para las secuencias :-(. Triste!