2011-01-31 17 views
6

Estoy tratando con una tabla en SQL Server que tiene una columna serial_no, que se define como una int no nula. No parece ser un campo de incremento automático, como si dejo esa columna fuera de mi declaración de inserción, recibo un error que dice que el campo serial_no no puede ser nulo.SQL Server: inserte siguiente disponible int

Entonces, ¿cómo puedo insertar el siguiente número disponible?

yo probamos este:

INSERT INTO mytable (serial_no) VALUES ((SELECT MAX(serial_no)+1 FROM mytable)) 

pero me da un error que dice que subconsultas no se pueden utilizar en este contexto.

EDITAR: Esta tabla se utiliza en un producto listo para usar, por lo que no puedo cambiar el diseño y hacer que la columna serial_no aumente automáticamente.

+0

¿Tiene que ser único en todas las filas? Los enfoques recomendados en las respuestas generalmente no son seguros para concurrencia, p. podrías terminar con duplicados ..... o necesitas a) hacer esto una columna 'INT IDENTITY' y dejar que SQL Server se preocupe por ello, b) hacer mucho más trabajo que los obviuos' SELECT MAX (serial_no) + 1', o c) esperar SQL Server 2011 y usar su concepto de 'SECUENCIAS' –

+0

marc_s, estoy totalmente de acuerdo, pero desafortunadamente no puedo cambiar el diseño de la base de datos. ¿Qué pasa si bloqueo la mesa, hago la inserción y luego la desbloqueo? ¿Eso lo protegería de crear un engañado? ¿O una consulta que está en espera simplemente espera a que se libere el bloqueo y luego inserta un # duplicado de todos modos? –

+1

¿Puede ejecutar un seguimiento para determinar cómo la aplicación de terceros crea el valor y simplemente hacer lo mismo? Diría que si no hay una columna de identidad, es probable que utilicen una tabla para contener el siguiente valor. Hay una respuesta que menciona esta técnica ya. –

Respuesta

9

Usted puede mejorar la concurrencia de escritura con sugerencias de bloqueo

INSERT INTO mytable (serial_no, value) 
SELECT MAX (serial_no)+1, @value 
FROM mytable WITH (ROWLOCK, XLOCK, HOLDLOCK) 

Si el rendimiento is't importante, tratar TABLOCKX en lugar de ROWLOCK, XLOCK

Sin embargo, dado que esto no es seguro, ya sea que usted necesita para volver a intentar

DECLARE @retry bit 
SET @retry = 1 

WHILE @Retry = 1 
BEGIN 
    BEGIN TRY 
     INSERT INTO mytable (serial_no, value) 
     SELECT MAX (serial_no)+1, @value 
     FROM mytable WITH (ROWLOCK, XLOCK, HOLDLOCK) 

     SET @Retry = 0 
    END TRY 
    BEGIN CATCH 
     IF ERROR_NUMBER() <> 2627 --PK violation 
      RAISERROR ('blah', 16, 1) 
    END CATCH 
END 

O cambiar a una columna de IDENTIDAD y hacerlo correctamente ...

+1

+1, aunque su última línea debería estar en ** negrita ** realmente. – cjk

+0

la identidad es la mejor opción cuando es posible. Sin embargo, hay casos en los que se necesita una clave única administrada manualmente. Este es el caso en el que estoy ahora. Un cliente quiere un FileNumber único, que es secuencial, editable por el usuario y cambia el comienzo de la secuencia todos los años – George

+0

@George: ahora tenemos secuencias en SQL Server 2012 para este tipo de cosas – gbn

2

El error se puede fijar al dejar caer los VALORES

INSERT INTO mytable (serial_no, value) 

SELECT MAX(serial_no)+1 , 
@value 
FROM mytable) 

pero esto es una mala idea. Hay una condición de carrera en MAX (serial_no) +1 (por ejemplo, dos Inserts obtienen el mismo valor para Max (Serial_no).

Le conviene usar un campo de incremento automático. También puede crear una tabla que almacene el actual siguiente valor e incrementarlo en lugar de utilizar como máximo

+1

+1 por mencionar la posibilidad de duplicados con este enfoque MAX() + 1; y sugiriendo la solución de autoincremento –

+0

El problema es que no es mi tabla o aplicación. No puedo cambiarlo –

0
INSERT INTO mytable (serial_no) SELECT MAX(serial_no)+1 FROM mytable 
+1

no es seguro - podría terminar con duplicados, si dos INSERT ocurren desde dos conexiones casi al mismo tiempo ... –

0

Inténtelo sin valores:.

INSERT INTO mytable (serial_no) SELECT MAX(serial_no)+1 FROM mytable 
+0

no es seguro - podría terminar con duplicados, si dos INSERT ocurren a partir de dos conexiones casi al mismo tiempo ... .. –

+0

Sí ... La fea solución aquí sería ejecutar el inserto en una transacción ... –

+2

no feo - ¡la única solución ** viable y segura **! –

Cuestiones relacionadas