SQL Server 2008 y hasta
En SQL Server 2008 y hasta, por supuesto, la forma más rápida es Convert(date, @date)
. Esto se puede convertir a datetime
o datetime2
si es necesario.
¿Qué es realmente mejor en SQL Server 2005 y versiones anteriores?
He visto afirmaciones inconsistentes sobre lo que es más rápido para truncar el tiempo de una fecha en SQL Server, e incluso algunas personas dijeron que lo hicieron, pero mi experiencia ha sido diferente. Así que hagamos algunas pruebas más estrictas y dejemos que todos tengan el guión, así que si cometo errores, la gente puede corregirme.
Conversiones flotador no son exactos
En primer lugar, me quedaría lejos de convertir a datetime
float
, porque no convierte correctamente. Puede salirse con la suya haciendo la eliminación del tiempo con precisión, pero creo que es una mala idea usarlo porque comunica implícitamente a los desarrolladores que esta es una operación segura y no es.Echar 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 deberíamos estar enseñando a la gente en nuestro código o en nuestros ejemplos en línea.
¡Además, ni siquiera es la manera más rápida!
Proof - Pruebas de rendimiento
Si desea realizar algunas pruebas a ti mismo para ver cómo los diferentes métodos realmente comparan, entonces usted necesita este script de configuració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
Tenga en cuenta que esto crea una tabla 427.57 MB en su base de datos y tardará unos 15-30 minutos en ejecutarse. Si su base de datos es pequeña y está configurada para un 10% de crecimiento, tomará más tiempo que si usted es lo suficientemente grande.
Ahora para el script de prueba de rendimiento real. Tenga en cuenta que es útil no devolver las filas al cliente, ya que es caro en 26 millones de filas y podría ocultar las diferencias de rendimiento entre los métodos.
Rendimiento Resultados
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 análisis trepador
Algunas notas sobre esto. En primer lugar, si solo realiza un GROUP BY o una comparación, no hay necesidad de volver a convertir a datetime
. Así que puedes ahorrar algo de CPU evitando eso, a menos que necesites el valor final para mostrarlo. Usted puede incluso GRUPO POR el valor sin convertir 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 tienen un poco más de tiempo para convertir de nuevo a datetime
, pero la conversión varchar
casi duplica? Esto revela la porción de la CPU que está dedicada al cálculo de la fecha en las consultas. Hay partes del uso de la CPU que no implican el cálculo de la fecha, y esto parece ser algo cercano a 19875 ms en las consultas anteriores. Luego, la conversión requiere un monto adicional, por lo que si hay dos conversiones, esa cantidad se utilizará aproximadamente dos veces.
Más examen revela que en comparación con Convert(, 112)
, la consulta Convert(, 101)
tiene algún gasto adicional de la CPU (ya que utiliza un varchar
ya?), Porque la segunda conversión de nuevo a date
no cuesta tanto como la conversión inicial a varchar
, pero con Convert(, 112)
está más cerca del mismo 20000 ms de costo base de la CPU.
Éstos son los cálculos en 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 la CPU para un ida y vuelta de nuevo a datetime
.
solo es el tiempo de CPU para una conversión única al tipo de datos alternativo (el que tiene el efecto secundario de eliminar la porción de tiempo).
base de es el cálculo de restar de single
la diferencia entre las dos invocaciones: single - (round - single)
. Es una cifra aproximada que asume que la conversión hacia y desde ese tipo de datos y datetime
es aproximadamente la misma en cualquier dirección. Parece que esta suposición no es perfecta, pero está cerca porque los valores son todos cercanos a 20000 ms con solo una excepción.
Una cosa más interesante es que el costo de base es casi igual a la sola Convert(date)
método (que tiene que ser casi 0 coste, como el servidor internamente puede extraer la parte de día entero a la derecha de los primeros cuatro bytes del tipo de datos datetime
).
Conclusión
Así que lo que parece es que el método varchar
la conversión de un solo sentido tarda unos 1,8 microsegundo y la dirección de un solo DateDiff
método tarda unos 0,18 mu s. Estoy basando esto en el tiempo más conservador de "CPU base" en mi prueba de 18458 ms total para 25,920,000 filas, entonces 23218 ms/25920000 = 0.18 μs. La aparente mejora de 10x parece mucho, pero francamente es bastante pequeña hasta que se ocupen de cientos de miles de filas (617k filas = 1 segundo de ahorro).
Incluso teniendo en cuenta esta pequeña mejora absoluta, en mi opinión, el método DateAdd
gana porque es la mejor combinación de rendimiento y claridad. La respuesta que requiere un "número mágico" de 0.50000004
va a morder a alguien algún día (cinco ceros o seis ???), además es más difícil de entender.
Notas adicionales
Cuando consigo un poco de tiempo voy a cambiar a 0.50000004
'12:00:00.003'
y ver cómo lo hace. Se convierte al mismo valor de datetime
y me resulta mucho más fácil de recordar.
Para los interesados, las pruebas anteriores se realizaron en un servidor donde @@ Version devuelve lo siguiente:
Microsoft SQL Server 2008 (RTM) - 10.0.1600.22 (Intel X86) jun 9 2008 14: 43:34 Derechos de autor (c) 1988-2008 Microsoft Corporation Standard Edition en Windows NT 5.2 (Build 3790: Service pack 2)
+1 ¿A qué versión de SQL Server lo probé por cierto? –
Parece que tiene * single * y * round * al revés en su mesa. Además, ¿hay alguna diferencia en el tiempo si usa 'char' en lugar de' varchar'? – Gabe
@Gabe gracias, arreglado. Char parece ser exactamente lo mismo que varchar. – ErikE