Pregunta

Esto es lo que yo uso:

SELECT CAST(FLOOR(CAST(getdate() as FLOAT)) as DATETIME)

Estoy pensando que puede haber un mejor y más elegante.

Requisitos:

  • Tiene que ser tan rápido como sea posible (menos de fundición, mejor).
  • El resultado final tiene que ser un datetime tipo, no una cadena.
¿Fue útil?

Solución

SQL Server 2008 y hasta

En SQL Server 2008 y hasta, por supuesto, la forma más rápida es Convert(date, @date).Esto puede ser lanzado de nuevo a un datetime o datetime2 si es necesario.

Lo que Es Realmente Mejor En SQL Server 2005 y Mayores?

He visto inconsistente de las afirmaciones acerca de lo que es más rápido para truncar el tiempo a partir de una fecha en SQL Server, y algunas personas incluso se dijo que lo hicieron pruebas, pero mi experiencia ha sido diferente.Así que vamos a hacer algunas pruebas más rigurosas y dejar que todo el mundo tiene el script así que si cometo algún error que la gente puede corregir mí.

Flotar Las Conversiones No Son Exactos

En primer lugar, me quedaría lejos de la conversión de datetime a float, porque no se convierten correctamente.Usted puede salirse con la suya haciendo que el tiempo de eliminación de la cosa con exactitud, pero creo que es una mala idea usar porque implícitamente se comunica a los desarrolladores de que esta es una operación segura y no es.Echa un vistazo:

declare @d datetime;
set @d = '2010-09-12 00:00:00.003';
select Convert(datetime, Convert(float, @d));
-- result: 2010-09-12 00:00:00.000 -- oops

Esto no es algo que debemos enseñar a la gente en nuestro código o en nuestros ejemplos en línea.

También, no es aún la forma más rápida!

Prueba – Pruebas De Rendimiento

Si desea realizar algunas pruebas para ver cómo los diferentes métodos realmente pila, entonces usted necesitará este script de instalación para ejecutar las pruebas más abajo:

create table AllDay (Tm datetime NOT NULL CONSTRAINT PK_AllDay PRIMARY KEY CLUSTERED);
declare @d datetime;
set @d = DateDiff(Day, 0, GetDate());
insert AllDay select @d;
while @@ROWCOUNT != 0
   insert AllDay
   select * from (
      select Tm =
         DateAdd(ms, (select Max(DateDiff(ms, @d, Tm)) from AllDay) + 3, Tm)
      from AllDay
   ) X
   where Tm < DateAdd(Day, 1, @d);
exec sp_spaceused AllDay;  -- 25,920,000 rows

Por favor, tenga en cuenta que esto crea un 427.57 MB tabla en su base de datos y tomar algo como 15 a 30 minutos para que se ejecute.Si su base de datos es pequeña y set de 10% de crecimiento tardará más que si el tamaño lo suficientemente grande como la primera.

Ahora para el desempeño real de prueba de secuencia de comandos.Por favor, tenga en cuenta que es un objetivo para no devolver las filas de vuelta al cliente como esto es una locura caro en 26 millones de filas y podría ocultar las diferencias de rendimiento entre los métodos.

Los Resultados De Rendimiento

set statistics time on;
-- (All queries are the same on io: logical reads 54712)
GO
declare
    @dd date,
    @d datetime,
    @di int,
    @df float,
    @dv varchar(10);

-- Round trip back to datetime
select @d = CONVERT(date, Tm) from AllDay; -- CPU time = 21234 ms,  elapsed time = 22301 ms.
select @d = CAST(Tm - 0.50000004 AS int) from AllDay; -- CPU = 23031 ms, elapsed = 24091 ms.
select @d = DATEDIFF(DAY, 0, Tm) from AllDay; -- CPU = 23782 ms, elapsed = 24818 ms.
select @d = FLOOR(CAST(Tm as float)) from AllDay; -- CPU = 36891 ms, elapsed = 38414 ms.
select @d = CONVERT(VARCHAR(8), Tm, 112) from AllDay; -- CPU = 102984 ms, elapsed = 109897 ms.
select @d = CONVERT(CHAR(8), Tm, 112) from AllDay; -- CPU = 103390 ms,  elapsed = 108236 ms.
select @d = CONVERT(VARCHAR(10), Tm, 101) from AllDay; -- CPU = 123375 ms, elapsed = 135179 ms.

-- Only to another type but not back
select @dd = Tm from AllDay; -- CPU time = 19891 ms,  elapsed time = 20937 ms.
select @di = CAST(Tm - 0.50000004 AS int) from AllDay; -- CPU = 21453 ms, elapsed = 23079 ms.
select @di = DATEDIFF(DAY, 0, Tm) from AllDay; -- CPU = 23218 ms, elapsed = 24700 ms
select @df = FLOOR(CAST(Tm as float)) from AllDay; -- CPU = 29312 ms, elapsed = 31101 ms.
select @dv = CONVERT(VARCHAR(8), Tm, 112) from AllDay; -- CPU = 64016 ms, elapsed = 67815 ms.
select @dv = CONVERT(CHAR(8), Tm, 112) from AllDay; -- CPU = 64297 ms,  elapsed = 67987 ms.
select @dv = CONVERT(VARCHAR(10), Tm, 101) from AllDay; -- CPU = 65609 ms, elapsed = 68173 ms.
GO
set statistics time off;

Algunos De Senderismo Análisis

Algunas notas acerca de esto.Primero de todo, si sólo la realización de un GRUPO o de una comparación, no hay necesidad de volver a convertir a datetime.Así que usted puede ahorrar algo de CPU, evitando que, a menos que necesite el valor final para fines de visualización.Usted puede incluso GRUPO de los no convertidos valor y poner la conversión sólo en la cláusula SELECT:

select Convert(datetime, DateDiff(dd, 0, Tm))
from (select '2010-09-12 00:00:00.003') X (Tm)
group by DateDiff(dd, 0, Tm)

También, ver cómo las conversiones numéricas sólo tomar un poco más de tiempo para volver a convertir a datetime, pero el varchar la conversión casi se duplica?Esto revela la parte de la CPU que se dedica a la fecha de cálculo en las consultas.Hay partes de la CPU que no involucra a la fecha de cálculo, y esto parece ser algo cercano a 19875 ms en las consultas anteriores.A continuación, la conversión se lleva una cantidad adicional, así que si hay dos conversiones, que la cantidad que se consume aproximadamente el doble.

Más examen revela que en comparación con Convert(, 112), el Convert(, 101) consulta algunos de CPU adicional de gastos (ya que se utiliza más varchar?), la segunda porque la conversión a date no cuesta tanto como la conversión inicial a varchar, pero con Convert(, 112) está más cerca de la misma 20000 ms CPU de la base de costo.

Aquí están los cálculos sobre el tiempo de CPU que he utilizado para el análisis anterior:

     method   round  single   base
-----------  ------  ------  -----
       date   21324   19891  18458
        int   23031   21453  19875
   datediff   23782   23218  22654
      float   36891   29312  21733
varchar-112  102984   64016  25048
varchar-101  123375   65609   7843
  • ronda es el tiempo de CPU para un viaje de ida y vuelta a datetime.

  • solo es tiempo de CPU para una sola conversión a un tipo de datos (el que tiene el efecto secundario de la extracción de la porción de tiempo).

  • base es el cálculo de la resta de single la diferencia entre las dos invocaciones: single - (round - single).Es una cifra aproximada que supone la conversión y a partir de ese tipo de datos y datetime es aproximadamente la misma en cualquier dirección.Parece que esta suposición no es perfecto, pero está cerca, porque los valores están todos cerca de 20000 ms, con una única excepción.

Una cosa más interesante es que la base de costo es casi igual a la de una sola Convert(date) método (que tiene que estar casi a coste 0, ya que el servidor puede internamente extraer el entero día parte a la derecha de los cuatro primeros bytes de la datetime tipo de datos).

Conclusión

Así que lo que parece es que el de una sola dirección varchar método de conversión se lleva a alrededor de 1,8 µs y una sola dirección DateDiff método toma alrededor de 0.18 µs.Me estoy basando esto en el más conservador de los "CPU de la base" el tiempo en mis pruebas de 18458 ms total para 25,920,000 filas, por lo que 23218 ms / 25920000 = 0.18 µs.La aparente 10x mejora parece mucho, pero es francamente bastante pequeño hasta que usted está tratando con cientos de miles de filas (617k filas = 1 segundo de ahorro).

Incluso teniendo en cuenta este pequeño absoluta de mejora, en mi opinión, el DateAdd método de la gana porque es la mejor combinación de rendimiento y claridad.La respuesta que requiere de un "número mágico" de 0.50000004 se va a morder a alguien algún día (cinco ceros o seis???), además de que es más difícil de entender.

Notas Adicionales

Cuando tengo algo de tiempo voy a cambiar 0.50000004 a '12:00:00.003' y a ver cómo lo hace.Se convierte a la misma datetime valor y me resulta mucho más fácil de recordar.

Para los interesados, las pruebas anteriores se ejecutan en un servidor donde @@Version devuelve el siguiente:

Microsoft SQL Server 2008 (RTM) - 10.0.1600.22 (Intel X86) Julio 9 De 2008 14:43:34 Copyright (c) 1988-2008 Microsoft Corporation Edición Estándar en Windows NT 5.2 (Build 3790:Service Pack 2)

Otros consejos

SQL Server 2008 tiene un nuevo fecha tipo de datos y esto simplifica este problema:

SELECT CAST(CAST(GETDATE() AS date) AS datetime)

Itzik Ben-Gan en DATETIME Cálculos, Parte 1 (SQL Server Magazine, febrero de 2007) muestra tres métodos para realizar dicha conversión (más lento al más rápido;la diferencia entre el segundo y el tercer método es pequeño):

SELECT CAST(CONVERT(char(8), GETDATE(), 112) AS datetime)

SELECT DATEADD(day, DATEDIFF(day, 0, GETDATE()), 0)

SELECT CAST(CAST(GETDATE() - 0.50000004 AS int) AS datetime)

Su técnica de la fundición a la flotador) es sugerido por un lector en la edición de abril de la revista.Según él, tiene un rendimiento comparable a la de la segunda técnica que se presentaron anteriormente.

Su CAST-FLOOR-CAST ya parece ser una forma óptima, al menos en MS SQL Server 2005.

Algunas otras soluciones que he visto tienen una conversión de cadena, como Select Convert(varchar(11), getdate(),101) en ellos, que es más lento por un factor de 10.

Por favor trate de:

SELECT CONVERT(VARCHAR(10),[YOUR COLUMN NAME],105) [YOURTABLENAME]

SQL2005:Recomiendo yeso en lugar de dateadd.Por ejemplo,

select cast(DATEDIFF(DAY, 0, datetimefield) as datetime)

promedio de alrededor de 10% más rápido en mi conjunto de datos, que

select DATEADD(DAY, DATEDIFF(DAY, 0, datetimefield), 0)

(y casting en smalldatetime fue aún más rápido)

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