2008-08-05 22 views

Respuesta

103

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 datetimefloat, 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)

+0

+1 ¿A qué versión de SQL Server lo probé por cierto? –

+0

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

+0

@Gabe gracias, arreglado. Char parece ser exactamente lo mismo que varchar. – ErikE

11

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

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

+2

Esta no es la manera óptima, por muy un poco. Por favor, vea [mi respuesta] (http://stackoverflow.com/questions/2775/whats-the-best-way-to-remove-the-time-portion-of-a-datetime-value-sql-server/3696991 # 3696991) en esta misma página. – ErikE

+0

Utilizamos el método sugerido por Michael Stum en uno de nuestros productos y funciona como un encanto. –

27

SQL Server 2008 tiene un nuevo date data type y esto simplifica este problema a:

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

Itzik Ben-Gan en DATETIME Calculations, Part 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 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 (fundición a float) es sugerida por un lector en la edición de abril de la revista. Según él, tiene un rendimiento comparable al de la segunda técnica presentada anteriormente.

+0

En mi opinión, lanzar para flotar no es lo mejor. Por favor [ver mi respuesta] (http://stackoverflow.com/questions/2775/whats-the-best-way-to-remove-the-time-portion-of-atime-value-sql-server/3696991 # 3696991) – ErikE

+0

@Emtucifor Estoy de acuerdo en que el tercer método es muy oscuro debido al valor * 0.50000004 *, pero ** es el más rápido y tus pruebas confirman que **. Por lo tanto, satisface el requisito * lo más rápido posible *. –

+0

@Emtucifor Además, esto es lo que dice el artículo que he vinculado sobre el * 0.50000004 * valor: * Aunque esta expresión es corta (y eficiente, como lo demostraré en breve), ** Tengo que decir que me siento incómodo con ella * *. No estoy seguro de poder decir exactamente por qué, tal vez porque es demasiado técnico y no se puede ver la lógica relacionada con la fecha. * –

3

por favor, intente:

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

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

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

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

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

(y fundición en smalldatetime fue más rápido todavía)

Cuestiones relacionadas