Pregunta

Tengo una aplicación que utiliza los números de incidentes (entre otros tipos de números). Estos números se almacenan en una tabla llamada "Number_Setup", que contiene el valor actual del contador.

Cuando la aplicación genera un nuevo incidente, mesa que number_setup y obtiene la fila contador del número requerido (contadores pueden reiniciarse diaria, semanal, etc., y se almacenan como int 's). A continuación, incremenets el contador y actualiza la fila con el nuevo valor.

La aplicación es multiusuario (aproximadamente 100 usuarios en un momento dado, así como trabajos de SQL que se ejecutan y agarrar 100 de los registros de incidentes y los números de solicitud de incidentes para cada uno). La tabla incidente tiene algunos números duplicados de incidentes en los que no deben ser duplicado.

A proc almacenado se utiliza para recuperar el siguiente contador.

SELECT @Counter = counter, @ShareId=share_id, @Id=id
FROM Number_Setup
WHERE LinkTo_ID=@LinkToId
AND Counter_Type='I'

IF isnull(@ShareId,0) > 0
BEGIN 
    -- use parent counter
    SELECT @Counter = counter, @ID=id
    FROM Number_Setup
    WHERE Id=@ShareID
END

SELECT @NewCounter = @Counter + 1

UPDATE Number_Setup SET Counter = @NewCounter
WHERE id=@Id

Ahora he rodeado de ese bloque con una transacción, pero no estoy del todo seguro de que' será 100% solucionar el problema, ya que creo que hay cerraduras siendo compartidos, por lo que el contador puede ser leído de todos modos.

Tal vez pueda comprobar que el contador no se ha actualizado, en la instrucción de actualización

UPDATE Number_Setup SET Counter = @NewCounter
WHERE Counter = @Counter
IF @@ERROR = 0 AND @@ROWCOUNT > 0 
    COMMIT TRANSACTION
ELSE
    ROLLBACK TRANSACTION

Estoy seguro de que este es un problema común con los números de factura en aplicaciones financieras, etc.
No puedo poner la lógica en el código y utilizar cualquiera de bloqueo a ese nivel. También he encerrado en HOLDLOCK pero no estoy seguro de ello de aplicación. En caso de que se incluyera en las dos instrucciones SELECT?

¿Cómo puedo asegurar que no hay duplicados se crean?

¿Fue útil?

Solución

El truco es hacer la actualización del contador y leer en una sola operación atómica:

UPDATE Number_Setup SET Counter = Counter+1
OUTPUT INSERTED.Counter 
WHERE id=@Id;

Esto aunque no asigna el nuevo contador a @NewCounter, sino que se devuelve como un conjunto de resultados al cliente. Si necesita asignar, utilizar una variable de tabla intermedia para dar salida a la nueva EN contador:

declare @NewCounter int;
declare @tabCounter table (NewCounter int);
UPDATE Number_Setup SET Counter = Counter+1
OUTPUT INSERTED.Counter INTO @tabCounter (NewCounter)
WHERE id=@Id
SELECT @NewCounter = NewCounter FROM @tabCounter;

Esto resuelve el problema de hacer que el incremento del contador atómica. Todavía tiene otras condiciones de carrera en su procedimiento porque el LinkTo_Id y share_id todavía pueden ser actualizados después de la primera seleccionada para que pueda incrementar el contador del enlace a un artículo equivocado, pero que no se pueden resolver sólo de este ejemplo de código, ya que depende también en el código que actualiza podía comprender la shared_id y / o LinkTo_Id.

Por cierto que debe obtener la costumbre de, nombre de sus campos con el caso consistente. Si son consistentemente nombrado entonces debe utilizar el caso de concordancia exacta de código T-SQL. Las secuencias de comandos ejecutar bien ahora sólo porque usted tiene un servidor sensitiva a colación, si se implementa en un servidor cotejo entre mayúsculas y minúsculas y las secuencias de comandos no hace coincidir el caso exacto de los errores de nombres de campos / cuadros seguirá en abundancia.

Otros consejos

¿Ha intentado utilizar GUID en lugar de autoincrements como su identificador único?

Si usted tiene la ablity modificar su trabajo que se mutiple registros, que iba a cambiar la forma de pensar para que su contador es una columna de identidad. Entonces, cuando llegue el siguiente registro se puede simplemente hacer una inserción y obtener el @@ identidad de la tabla. Eso sería garantizar que se obtiene el número más grande. También tendría que hacer un dbccReseed para restablecer el contador en lugar de sólo la actualización de la tabla cuando se desea restablecer la identidad. El único problema es que tendría que hacer 100 o más inserciones como parte de su trabajo SQL para obtener un grupo de identidades. Eso puede ser demasiado trabajo, pero utilizando una columna de identidad es una forma guarenteed para obtener números únicos.

puede ser que falte algo, pero parece que usted está tratando de reinventar la tecnología que ya ha sido resuelto por la mayoría de las bases de datos.

en lugar de la lectura y la actualización de la columna 'Contador' en la tabla Number_Setup, ¿por qué no se utiliza una clave principal autoincremental para su contador? Usted nunca tendrá un valor duplicado de una llave primaria.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top