2010-04-12 17 views
5

Tengo una situación en la que dos personas pueden trabajar en el mismo orden (almacenado en una base de datos MS SQL) desde dos computadoras diferentes. Para evitar la pérdida de datos en el caso de que uno guarde primero su copia del pedido, y luego un poco más tarde el segundo guardará su copia y sobrescribirá el primero, he agregado un cheque en el campo lastSaved (datetime) antes ahorro.MS SQL problema de precisión de fecha y hora

El código es más o menos así:

private bool orderIsChangedByOtherUser(Order localOrderCopy) 
{ 
    // Look up fresh version of the order from the DB 
    Order databaseOrder = orderService.GetByOrderId(localOrderCopy.Id); 

    if (databaseOrder != null && 
     databaseOrder.LastSaved > localOrderCopy.LastSaved) 
    { 
     return true; 
    } 
    else 
    { 
     return false; 
    } 
} 

Esto funciona para la mayoría de las veces, pero he encontrado un pequeño fallo.

Si orderIsChangedByOtherUser vuelve falsa, la copia local tendrá su lastSaved actualizado a la hora actual y luego ser persistido a la base de datos. El valor de lastSaved en la copia local y la base de datos ahora debería ser la misma. Sin embargo, si orderIsChangedByOtherUser se ejecuta nuevamente, a veces devuelve true aunque ningún otro usuario haya realizado cambios en la base de datos.

Al depurar en Visual Studio, databaseOrder.LastSaved y localOrderCopy.LastSaved parecen tener el mismo valor, pero cuando se mira más de cerca que a veces difieren en unos pocos milisegundos.

me encontré con un this article corto plazo sobre la precisión de milisegundos para la fecha y hora en SQL:

Otro problema es que SQL Server tiendas DATETIME con una precisión de milisegundos (3,33 0. 00333 segundos).

La solución que se me ocurrió para este problema es comparar las dos fechas y considerarlas iguales si difieren en menos de, por ejemplo, 10 milisegundos.

Mi pregunta es: ¿hay alguna forma mejor/más segura de comparar dos valores de fecha y hora en MS SQL para ver si son exactamente lo mismo que?

+0

Solo para especificar: No tengo la opción de cambiar el tipo del campo * lastSaved *, así que tendré que mantenerme actualizado con datetime. – Nailuj

Respuesta

4

Sé que dijo que no se puede cambiar el tipo, pero si esto es sólo para mantener la compatibilidad & su uso de 2008 podría cambiar el campo lastSaved-DATETIME2 (que es totalmente compatible con DATETIME) y el uso SYSDATETIME() ambos de los cuales tener una precisión mucho mayor.

+0

No tenía conocimiento de datetime2. Mis servidores de prueba están ejecutando al menos SQL Server 2008, por lo que podría ser una opción. Solo tengo que asegurarme de que todos los que usan el sistema también estén en 2008 ... Gracias :) – Nailuj

0

Puede usar un campo de marca de tiempo para verificar la fecha de la última edición en lugar de un campo de fecha y hora? (En SQL 2008 esto ahora es RowVersion)

+0

Podría ser una posibilidad, pero no tengo la opción de cambiar el tipo del último campo guardado. – Nailuj

+0

No es necesario. Agregue un campo adicional para eso. A continuación, modifique UPDATE SP para verificar tanto en el campo PK como en el campo de la versión de la fila. Obtendrá 0 filas afectadas si la versión de la fila ha cambiado entonces. –

0

TIENES QUE ASEGURARSE DE QUE LAS PRECISIONES DE TIEMPO SE ALINEEN. ESTO ES MAYORMENTE PUESTO POSIENDO LA LÓGICA CORRECTA EN EL LADO C# PARA REDUCIR LA PRECISIÓN BAJO LA QUE ES ORIGINAL EN EL DÍA. objeto: básicamente, haz que suer tengas, por ejemplo, marcas de tiempo siempre en segundos, no más abajo, en todas las capas.

Si lo hace correctamente, la marca de tiempo en todas las capas será inmediatamente comparable.

2

Puede agregar un campo de revisión entero a su tabla de pedidos. Cada vez que un usuario guarda el pedido, aumenta la revisión en uno. Entonces es fácil verificar si alguien ha alterado el orden o si el usuario que desea guardar el pedido está en la última revisión.

1

Mientras se encuentre dentro de SQL 2005 y antes de que el problema de precisión siempre estará presente, nunca será más preciso que 1/300 de un segundo, o 3.33ms.

Independientemente de la falta de precisión, está programando una condición de carrera defectuosa en la que ambos usuarios aún pueden escribir en la base de datos en una sucesión rápida, pero ambos se consideran correctos. La falta de precisión aumenta las posibilidades de que ocurra, siempre y cuando la verificación y las escrituras posteriores ocurran dentro de los mismos 3-4 ms.

Cualquier intento de comprobar seguido de una escritura sufre este problema, y ​​o bien tiene que aceptar las consecuencias de un bloqueo optimista, cambiar el bloqueo a pessemistic o implementar algún tipo de estrategia de tipo de semáforo para manejar el bloqueo correctamente.

+0

Soy consciente de la lógica potencialmente defectuosa, pero como con muchos sistemas antiguos, es algo con lo que uno tiene que vivir. Tenía una pequeña esperanza de que pudiera haber una forma de evitarlo, pero creo que podríamos vivir con una precisión de 0.33ms. – Nailuj

0

Hay otra manera de hacer esto potencialmente.

En lugar de pasar el "Último guardado" de la máquina local, modifique el procedimiento almacenado ACTUALIZADO. Asignar LastSaved = getdate() A continuación, devuelva el valor de LastSaved (y cualquier otro resultado, como ID) y actualice la hora de LastSaved en el cliente con ese resultado.

Hay dos ventajas obvias para esto. Una es que se conserva la precisión de DateTime. La otra es que la fecha y la hora ahora son consistentes con el servidor, en lugar de sufrir problemas de latencia de red y deriva del reloj local.

Normalmente usamos SP generados automáticamente para las operaciones CRUD, y las tablas usualmente ejecutan los pares "Creado/Última Actualización" y "Creado/LastUpdatedBy" donde las fechas están establecidas en el servidor, y los valores "Por" son pasados, y si NULL están configurados en System_User

Cuestiones relacionadas