Brechas inesperadas en la columna IDENTIDAD


18

Estoy tratando de generar números de pedido de compra únicos que comienzan en 1 y se incrementan en 1. Tengo una tabla PONumber creada con este script:

CREATE TABLE [dbo].[PONumbers]
(
  [PONumberPK] [int] IDENTITY(1,1) NOT NULL,
  [NewPONo] [bit] NOT NULL,
  [DateInserted] [datetime] NOT NULL DEFAULT GETDATE(),
  CONSTRAINT [PONumbersPK] PRIMARY KEY CLUSTERED ([PONumberPK] ASC)    
);

Y un procedimiento almacenado creado con este script:

CREATE PROCEDURE [dbo].[GetPONumber] 
AS
BEGIN
    SET NOCOUNT ON;

    INSERT INTO [dbo].[PONumbers]([NewPONo]) VALUES(1);
    SELECT SCOPE_IDENTITY() AS PONumber;
END

En el momento de la creación, esto funciona bien. Cuando se ejecuta el procedimiento almacenado, comienza en el número deseado y se incrementa en 1.

Lo extraño es que, si apago o hiberno mi computadora, la próxima vez que se ejecute el procedimiento, la secuencia ha avanzado en casi 1000.

Vea los resultados a continuación:

Números de PO

¡Puedes ver que el número saltó de 8 a 1002!

  • ¿Por qué está pasando esto?
  • ¿Cómo me aseguro de que los números no se salten así?
  • Todo lo que necesito es que SQL genere números que son:
    • a) Garantizado único.
    • b) incrementar en la cantidad deseada.

Admito que no soy un experto en SQL. ¿No entiendo lo que hace SCOPE_IDENTITY ()? ¿Debería estar usando un enfoque diferente? Investigué las secuencias en SQL 2012+, pero Microsoft dice que no se garantiza que sean únicas por defecto.

Respuestas:


25

Este es un problema conocido y esperado: la forma en que SQL Server administra las columnas IDENTITY ha cambiado en SQL Server 2012 ( algunos antecedentes ); de forma predeterminada almacenará en caché 1000 valores y si reinicia SQL Server, reinicia el servidor, falla, etc., tendrá que arrojar esos 1000 valores, porque no tendrá una forma confiable de saber cuántos de ellos fueron realmente emitido. Esto está documentado aquí . Hay un indicador de seguimiento que cambia este comportamiento de tal manera que cada asignación de IDENTIDAD se registra *, evitando esos vacíos específicos (pero no huecos de retrocesos o eliminaciones); Sin embargo, es importante tener en cuenta que esto puede ser bastante costoso en términos de rendimiento, por lo que ni siquiera voy a mencionar el indicador de seguimiento específico aquí.

* (Personalmente, creo que este es un problema técnico que podría resolverse de manera diferente, pero como no escribo el motor, no puedo cambiarlo).

Para tener claro cómo funcionan IDENTIDAD y SECUENCIA:

  • No se garantiza que ninguno sea único (debe aplicarlo a nivel de tabla, utilizando una clave primaria o una restricción única)
  • Ninguno de los dos está garantizado sin espacios (cualquier reversión o eliminación, por ejemplo, producirá un espacio, a pesar de este problema específico)

La singularidad es fácil de hacer cumplir. Evitar huecos no lo es. Debe determinar qué tan importante es para usted evitar estas brechas (en teoría, no debería preocuparse por las brechas en absoluto, ya que los valores de IDENTIDAD / SECUENCIA deben ser claves sustitutas sin sentido). Si es muy importante, entonces no debería usar ninguna de las implementaciones, sino más bien rodar su propio generador de secuencia serializable (vea algunas ideas aquí , aquí y aquí ), solo tenga en cuenta que matará la concurrencia.

Muchos antecedentes sobre este "problema":


Esta respuesta (excepto la parte del "indicador de rastreo") también se aplica a la mayoría de las demás bases de datos SQL (las que tienen secuencias de todos modos).
mustaccio

Gracias por la respuesta. La singularidad es el requisito más importante. Las brechas no son un gran problema, siempre que no sean grandes. por ejemplo, pasar de 1 a 4 sería aceptable, pero de 4 a 1003 no lo sería.
Ege Ersoz

1
Versión corta: los valores de ID se utilizarán como números de orden de compra. El cliente ejecuta informes mensuales y quiere saber rápidamente cuántas OP se enviaron ese mes simplemente mirando el número de PO. Por lo tanto, no podemos hacer que aumente en ~ 1000 (hay un mantenimiento semanal en el que todos los servidores, incluido el servidor DB, se reinician).
Ege Ersoz

3
¿Por qué no les das un informe muy sencillo que solo usa ROW_NUMBER () OVER (PARTICIÓN POR ORDEN POR MES POR ID)? Una vez más, el número de identificación no debería tener sentido, esa es una forma terrible de mirar cuántas órdenes se tomaron. ¿Qué sucede si tiene un error en su código que elimina 1000 filas o revierte 275 transacciones, o 500 pedidos se cancelan legítimamente?
Aaron Bertrand

1
@Ege: "... diga cuántos ... con solo mirar el número de pedido". Tus usuarios se sentirán decepcionados. Los valores de identidad simplemente no funcionan de esa manera, ni usted (o ellos) deberían hacer tal suposición. ¿Único? Si. ¿Consecutivo? No. La forma correcta de contar las órdenes de compra enviadas durante un mes es ... contar la cantidad de órdenes de compra planteadas durante ese mes, según un campo de fecha [inalterable] en cada registro.
Phill W.

-4

Este es un problema de SQL Server. Todo lo que puedes hacer es reiniciar la columna.

elimine las entradas con el ID de columna incorrecto. Restablecer la identidad de la columna. Y luego la siguiente entrada tiene la identificación adecuada.

Restablecer identidad usando el siguiente comando sql: DBCC CHECKIDENT ('YOUR_TABLE_NAME', RESEED, 9)- 9 es la última identificación correcta


1
¿Qué quieres decir con "eliminar las entradas"?
ypercubeᵀᴹ

2
Hmmm ... parece que eliminar entradas podría conducir a la pérdida de datos.
Michael Green
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.