¿Cómo eliminar la porción de tiempo de un valor de fecha y hora (SQL Server)?

votos
77

Esto es lo que uso:

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

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

Requisitos:

  • Tiene que ser lo más rápido posible (cuanto menos casting, mejor).
  • El resultado final debe ser un datetimetipo, no una cadena.
Publicado el 05/08/2008 a las 21:08
fuente por usuario
En otros idiomas...                            


6 respuestas

votos
11

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

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

Respondida el 05/08/2008 a las 21:12
fuente por usuario

votos
27

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

SELECT CAST(CAST(GETDATE() AS date) AS datetime)
Respondida el 06/08/2008 a las 07:44
fuente por usuario

votos
16

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

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 para flotar ) 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.

Respondida el 06/08/2008 a las 09:06
fuente por usuario

votos
106

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 de nuevo a una datetimeo datetime2, si es necesario.

Lo que es realmente mejor en SQL Server 2005 y mayores?

He visto reclamos contradictorios en relación con lo que es más rápido para truncar el tiempo transcurrido desde la fecha en SQL Server, y algunas personas incluso dijeron que lo hicieron las pruebas, pero mi experiencia ha sido diferente. Así que vamos a hacer algunas pruebas más rigurosas y que todo el mundo tiene la secuencia de comandos por lo que si ningún error la gente puede corregirme.

Conversiones de flotación no son exactos

En primer lugar, me quedaría lejos de convertir datetimea float, porque no convierte correctamente. Usted puede salirse con hacer lo de eliminación de tiempo con precisión, pero creo que es una mala idea para utilizarlo porque implícitamente se comunica a los desarrolladores que esta es una operación segura y que 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!

Prueba - 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 se llevará a algo así como 15-30 minutos para correr. Si su base de datos es pequeño y dejar a un crecimiento del 10% se necesitará más tiempo que si usted tamaño lo suficientemente grande primero.

Ahora, para el guión real de las pruebas de rendimiento. Tenga en cuenta que se trata de un propósito de no volver filas de vuelta al cliente ya que esto es una locura caro en 26 millones de filas y podría ocultar las diferencias de rendimiento entre los métodos.

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 análisis Rambling

Algunas notas sobre esto. En primer lugar, si acaba de realizar un GROUP BY o una comparación, no hay necesidad de convertir de nuevo a datetime. Así se puede ahorrar algo de CPU, evitando que, a menos que necesite el valor final para fines de visualización. 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 varcharconversión es casi el doble? Esto revela la parte de la CPU que se dedica 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. A continuación, la conversión se lleva una cierta cantidad adicional, de modo que si hay dos conversiones, esa cantidad se utiliza hasta aproximadamente el doble.

Más examen revela que en comparación con Convert(, 112)la Convert(, 101)consulta tiene algún gasto adicional de la CPU (ya que utiliza una ya varchar?), Porque la segunda conversión de nuevo a dateno cuesta tanto como la conversión inicial a varchar, pero con Convert(, 112)que está más cerca de la misma 20000 costo base ms CPU.

Aquí son aquellos 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 la CPU para un ida y vuelta de nuevo a datetime.

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

  • la base es el cálculo de restar de singlela diferencia entre las dos invocaciones: single - (round - single). Es una cifra aproximada que asume la conversión hacia y desde dicho tipo de datos y datetimees aproximadamente el mismo en cualquier dirección. Al parecer, esta suposición no es perfecta, pero está cerca porque los valores están cerca de 20000 m con una sola 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, ya que el servidor puede extraer internamente la parte de día entero a la derecha de los primeros cuatro bytes del datetimetipo de datos).

Conclusión

Así que lo que parece es que la dirección de un solo varcharmétodo de conversión tarda unos 1,8 microsegundo y la dirección de un solo DateDiffmétodo de toma alrededor de 0,18 mu s. Estoy basando esto en el más conservador de tiempo "de la CPU de base" en mi prueba de 18458 ms total de 25,920,000 filas, por lo que ms / 23218 25920000 mu s = 0,18. La aparente mejora 10x parece mucho, pero es francamente muy pequeño hasta que se trata de cientos de miles de filas (617k filas = 1 segundo de ahorro).

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

Notas adicionales

Cuando consigo un poco de tiempo voy a cambiar 0.50000004a '12:00:00.003'y ver cómo lo hace. Se convierte en el mismo datetimevalor 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)

Respondida el 12/09/2010 a las 23:57
fuente por usuario

votos
3

Por favor, inténtalo:

SELECT CONVERT(VARCHAR(10),[YOUR COLUMN NAME],105) [YOURTABLENAME]
Respondida el 29/06/2013 a las 10:49
fuente por usuario

votos
0

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

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

un 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 era todavía más rápido)

Respondida el 05/11/2014 a las 04:26
fuente por usuario

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more