2010-11-29 13 views
9

Me encontré con algo que parece extraño. SQL Server parece estar redondeando algunos valores DateTime de forma inapropiada cuando los guardo en columnas datetime. Sospecho que me estoy perdiendo algo, pero no puedo encontrarlo. Estoy ejecutando esta prueba contra SQL Server 2008 usando .NET 4.0. Lo siguiente debe ilustrar el problema:Redondeo no deseado de DateTime en SQL Server

He creado una tabla en SQL Server llamada Timestamps. Tiene dos columnas:

Identificación - bigint, Identidad, PK
marca de tiempo - fecha y hora

También creé una prueba simple que hace lo siguiente:

  1. Obtiene la hora actual , truncando el valor a una precisión de milisegundos
  2. Se guardó el tiempo truncado en Timestamps
  3. Recuperó el valor datetime` de la base de datos y lo comparó con el objeto DateTime original (truncado).
public static void RoundTest() 
{ 
    DateTime preTruncation = DateTime.UtcNow; 
    DateTime truncated = preTruncation.TruncateToMilliseconds(); 

    using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["test"].ConnectionString)) 
    { 
     conn.Open(); 
     SqlCommand cmd = new SqlCommand(@"INSERT INTO Timestamps(timestamp) 
              VALUES(@savedTime); 
              SELECT SCOPE_IDENTITY() AS id"); 
     cmd.Parameters.Add(new SqlParameter("savedTime", truncated)); 
     cmd.Connection = conn; 
     var id = cmd.ExecuteScalar(); 

     SqlCommand get = new SqlCommand(@"SELECT timestamp FROM Timestamps 
              WHERE id = @id"); 

     get.Parameters.Add(new SqlParameter("id", id)); 
     get.Connection = conn; 
     DateTime retrieved = (DateTime)get.ExecuteScalar(); 

     if (retrieved != truncated) 
     { 
      Console.WriteLine("original: " + preTruncation.TimeOfDay); 
      Console.WriteLine("truncated: " + truncated.TimeOfDay); 
      Console.WriteLine("retrieved: " + retrieved.TimeOfDay); 
      Console.WriteLine(); 
     } 
    } 
} 

Aunque espero que el valor truncado sea equivalente al valor devuelto de nuevo a partir de la base de datos, que no siempre es el caso. Aquí está un ejemplo de salida:

original: 19:59:13.4049965 
truncated: 19:59:13.4040000 
retrieved: 19:59:13.4030000 

original: 19:59:14.4989965 
truncated: 19:59:14.4980000 
retrieved: 19:59:14.4970000 

original: 19:59:15.4749965 
truncated: 19:59:15.4740000 
retrieved: 19:59:15.4730000 

original: 19:59:30.1549965 
truncated: 19:59:30.1540000 
retrieved: 19:59:30.1530000 

TruncateToMilliseconds() se parece a esto:

public static DateTime TruncateToMilliseconds(this DateTime t) 
{ 
    return new DateTime(t.Year, t.Month, t.Day, t.Hour, t.Minute, t.Second, t.Millisecond); 
} 

Lo que da? ¿Es esto realmente inadecuado redondear, o estoy haciendo una suposición errónea aquí?

Respuesta

13

La fecha y hora es solo exacta a 3ms. Por lo tanto, redondeará al múltiplo más cercano de 3 ms. Para superar esto, mira el datetime2. Tenga en cuenta que esto es solo para SQL2008 +

EDITAR: no es solo de 3ms. Se redondea a incrementos de .000, .003 o .007 segundos

+1

En realidad, almacena una fecha y hora en dos enteros de 4 bytes, el primero que representa la fecha y el segundo número entero de 4 bytes que representa la cantidad de marcas de reloj desde la medianoche. (aproximadamente 3.33 milésegundos cada uno) –

+0

Correcto. Termina siendo .003, .007 o .000 –

+0

¡Gracias! Simplemente asumí que datetime era preciso a 1 ms, ya que tiene una precisión de 1 ms. – Odrade

Cuestiones relacionadas