¿Cómo puedo truncar una fecha y hora en SQL Server?

281

¿Cuál es la mejor manera de truncar un valor de fecha y hora (para eliminar horas, minutos y segundos) en SQL Server 2008?

Por ejemplo:

declare @SomeDate datetime = '2009-05-28 16:30:22'
select trunc_date(@SomeDate)

-----------------------
2009-05-28 00:00:00.000
Julio Cesar
fuente

Respuestas:

495

Esto continúa reuniendo con frecuencia votos adicionales, incluso varios años después, por lo que necesito actualizarlo para las versiones modernas de SQL Server. Para Sql Server 2008 y posterior, es simple:

cast(getDate() As Date)

Tenga en cuenta que los últimos tres párrafos cerca de la parte inferior todavía se aplican, y a menudo necesita dar un paso atrás y encontrar una manera de evitar el yeso en primer lugar.

Pero también hay otras formas de lograr esto. Aquí están los más comunes.

La forma correcta (nuevo desde Sql Server 2008):

cast(getdate() As Date)

La forma correcta (antigua):

dateadd(dd, datediff(dd,0, getDate()), 0)

Esto es más antiguo ahora, pero aún vale la pena saberlo porque también puede adaptarse fácilmente a otros puntos de tiempo, como el primer momento del mes, minuto, hora o año.

Esta forma correcta utiliza funciones documentadas que son parte del estándar ansi y se garantiza que funcionan, pero puede ser algo más lento. Funciona al encontrar cuántos días hay desde el día 0 hasta el día actual, y agregar esos días de regreso al día 0. Funcionará sin importar cómo se almacene su fecha y hora y cuál sea su ubicación.

La manera rápida:

cast(floor(cast(getdate() as float)) as datetime)

Esto funciona porque las columnas de fecha y hora se almacenan como valores binarios de 8 bytes. Conviértalos en flotantes, vuélvalos a piso para eliminar la fracción y la parte de tiempo de los valores desaparecerá cuando los devuelva a la fecha y hora. Todo cambia un poco sin una lógica complicada y es muy rápido.

Tenga en cuenta que esto se basa en un detalle de implementación que Microsoft puede cambiar en cualquier momento, incluso en una actualización automática del servicio. Tampoco es muy portátil. En la práctica, es muy poco probable que esta implementación cambie en el corto plazo, pero aún es importante tener en cuenta el peligro si elige usarla. Y ahora que tenemos la opción de emitir como fecha, rara vez es necesario.

La forma incorrecta:

cast(convert(char(11), getdate(), 113) as datetime)

La forma incorrecta funciona convirtiendo a una cadena, truncando la cadena y volviendo a convertir a una fecha y hora. Está mal , por dos razones: 1) podría no funcionar en todos los entornos locales y 2) se trata de la forma más lenta posible de hacer esto ... y no solo un poco; Es como un orden de magnitud o dos más lento que las otras opciones.


Actualización Esto ha estado recibiendo algunos votos últimamente, por lo que quiero agregar que desde que publiqué esto, he visto algunas pruebas bastante sólidas de que SQL Server optimizará la diferencia de rendimiento entre la forma "correcta" y la "rápida". , lo que significa que ahora deberías favorecer a los primeros.

En cualquier caso, desea escribir sus consultas para evitar la necesidad de hacerlo en primer lugar . Es muy raro que haga este trabajo en la base de datos.

En la mayoría de los lugares, la base de datos ya es su cuello de botella. En general, es el servidor más caro para agregar hardware para mejorar el rendimiento y el más difícil para lograr esas adiciones correctamente (por ejemplo, debe equilibrar los discos con la memoria). También es el más difícil de escalar hacia afuera, tanto técnicamente como desde el punto de vista comercial; técnicamente es mucho más fácil agregar un servidor web o de aplicaciones que un servidor de base de datos e incluso si eso fuera falso, no paga más de $ 20,000 por licencia de servidor por IIS o apache.

El punto que estoy tratando de hacer es que, siempre que sea posible, debe hacer este trabajo a nivel de aplicación. El único momento en que debería encontrarse truncando una fecha y hora en Sql Server es cuando necesita agrupar por día, e incluso entonces probablemente debería tener una columna adicional configurada como una columna calculada, mantenida en el momento de inserción / actualización, o mantenida en lógica de aplicación. Saque de su base de datos este trabajo de alto índice de procesamiento de la CPU.

Joel Coehoorn
fuente
66
la "forma rápida" sigue siendo la forma más rápida para sql 2008 según un punto de referencia que acabo de ejecutar
Sam Saffron
3
FYI: stackoverflow.com/q/1177449/27535 y stackoverflow.com/q/133081/27535 La fecha / fecha / fecha "gana ...". Para una sola variable, a quién le importa, por supuesto, y uno espera que haya calculado columnas o más de un millón de filas :-)
gbn
9
Esta forma "correcta" solo funciona accidentalmente. La forma en que se escribe es como si la sintaxis de DateAdd fuera (intervalo, fecha, incremento), pero no lo es. Es (intervalo, incremento, fecha). Me topé con esto cuando intenté truncar una fecha al primero del mes: SELECT DATEADD (m, 0, DATEDIFF (m, 0, GETDATE ())) no funciona, pero SELECT DATEADD (m, DATEDIFF (m, 0, GETDATE ()), 0) hace. Al menos, esto es lo que veo en 2008R2.
Kelly Cline
1
@Kelly en 2008R2, ¿por qué no solo cast(getdate() as date)?
Joel Coehoorn
2
Que todo el trabajo en una columna de fecha y hora. getdate()Aquí hay un sustituto para cualquier fuente de fecha y hora que pueda tener.
Joel Coehoorn
44

Solo para SQL Server 2008

CAST(@SomeDateTime AS Date) 

Luego devuélvelo a datetime si quieres

CAST(CAST(@SomeDateTime AS Date) As datetime)
DJ.
fuente
Buen punto: todavía estoy en 2005 y, por lo tanto, para 2008 esta es probablemente la nueva forma "correcta" e incluso puede coincidir con el rendimiento de la manera "rápida".
Joel Coehoorn
1
El rendimiento de esta nueva forma es aún más rápido que la forma "rápida".
ErikE
21

Solo por el bien de una respuesta más completa, aquí hay una forma de trabajar para truncar cualquiera de las partes de fecha e incluir minutos (reemplazar GETDATE()con la fecha para truncar).

Esto es diferente de la respuesta aceptada en que puede usar no solo dd(días), sino cualquiera de las partes de la fecha (ver aquí ):

dateadd(minute, datediff(minute, 0, GETDATE()), 0)

Tenga en cuenta que en la expresión anterior, el 0es una fecha constante al comienzo de un año (1900-01-01). Si necesita truncar a partes más pequeñas, como segundos o milisegundos, debe tomar una fecha constante que esté más cerca de la fecha a truncar para evitar un desbordamiento.

Lucero
fuente
1
Esto fue monstruosamente útil. Busqué por todas partes una forma de truncar la fecha y hora en un lugar más bajo que el día completo.
Michael - ¿Dónde está Clay Shirky?
1
@ Michael, gracias por los comentarios, ¡es bueno saber que te ayudó!
Lucero
1
+1 esto debería tener más votos a favor, es una gran respuesta que se expande en la respuesta seleccionada.
jtate
1
Solo para que Internet lo sepa, no tiene que estar restringido a períodos completos de fechas. Aquí hay un ejemplo para intervalos de 15 minutos, usando la división de enteros:dateadd(minute, datediff(minute, 0, GETDATE()) / 15 * 15, 0)
Michael - ¿Dónde está Clay Shirky?
7

El fragmento que encontré en la web cuando tuve que hacer esto fue:

 dateadd(dd,0, datediff(dd,0, YOURDATE))
 e.g.
 dateadd(dd,0, datediff(dd,0, getDate()))
Tom Ritter
fuente
Estoy en 2005, pero pensé que 2008 tenía alguna función nueva para esto.
KM.
2
¡Ordenado! Hubiera recurrido a dividir las partes de la fecha y usar el manejo de cadenas para volver a unirlas. Puede no ser relevante, pero SQL2008 tiene un tipo de datos de solo fecha pura sin un elemento de tiempo.
Frans
1
Y tenga en cuenta que tiene los operandos DateAdd mezclados, es DateAdd(dd, DateDiff(...), 0). Esto puede morderte si no tienes cuidado.
ErikE
1

En SQl 2005 su función trunc_date podría escribirse así.

(1)

CREATE FUNCTION trunc_date(@date DATETIME)
RETURNS DATETIME
AS
BEGIN
    CAST(FLOOR( CAST( @date AS FLOAT ) )AS DATETIME)
END

El primer método es mucho más limpio. Utiliza solo 3 llamadas a métodos, incluido el CAST final () y no realiza la concatenación de cadenas, que es un plus automático. Además, no hay modelos de gran tamaño aquí. Si puede imaginar que las marcas de fecha / hora se pueden representar, la conversión de fechas a números y viceversa es un proceso bastante fácil.

(2)

CREATE FUNCTION trunc_date(@date DATETIME)
RETURNS DATETIME
AS
BEGIN
      SELECT CONVERT(varchar, @date,112)
END

Si le preocupa la implementación de fecha y hora de Microsoft (2) o (3) podría estar bien.

(3)

CREATE FUNCTION trunc_date(@date DATETIME)
RETURNS DATETIME
AS
BEGIN
SELECT CAST((STR( YEAR( @date ) ) + '/' +STR( MONTH( @date ) ) + '/' +STR( DAY(@date ) )
) AS DATETIME
END

Tercero, el método más detallado. Esto requiere dividir la fecha en sus partes de año, mes y día, uniéndolas en formato "aaaa / mm / dd", y luego volviendo a una fecha. Este método implica 7 llamadas a métodos, incluido el CAST final (), sin mencionar la concatenación de cadenas.

AlejandroR
fuente
1
CONVERT(DATE, <yourdatetime>) or CONVERT(DATE, GetDate()) or CONVERT(DATE, CURRENT_TIMESTAMP)
Decano
fuente
0

Para aquellos de ustedes que vinieron aquí buscando una forma de truncar un campo DATETIME a algo menos de un día completo, por ejemplo, cada minuto, pueden usar esto:

SELECT CAST(FLOOR(CAST(GETDATE() AS FLOAT)) + (FLOOR((CAST(GETDATE() AS FLOAT) - FLOOR(CAST(GETDATE() AS FLOAT))) * 1440.0) + (3.0/86400000.0)) / 1440.0 AS DATETIME)

así que si hoy fuera 2010-11-26 14:54:43.123entonces esto volvería 2010-11-26 14:54:00.000.

Para cambiar el intervalo al que hace la truca, reemplace 1440.0 con el número de intervalos en un día, por ejemplo:

24hrs          =   24.0  (for every hour)
24hrs / 0.5hrs =   48.0  (for every half hour)
24hrs / (1/60) = 1440.0  (for every minute)

(Siempre ponga un .0extremo para lanzar implícitamente a un flotador).


Para aquellos de ustedes que se preguntan para qué (3.0/86400000)sirve mi cálculo, SQL Server 2005 no parece emitir FLOATcon DATETIMEprecisión, por lo que esto agrega 3 milisegundos antes de colocarlo en el piso.

BG100
fuente
1
Sin embargo, tenga cuidado con los errores de redondeo debido a los límites de precisión de coma flotante ... además, esto no funciona con el datetime2tipo de datos.
Lucero
Para Hora, SELECT DATEADD (hora, DATEDIFF (hora, 0, GETDATE ()), 0) también funciona. Minuto, también, pero Segundo resultará en un desbordamiento.
Kelly Cline
El envío para flotar y volver a la fecha y hora no funciona correctamente .
ErikE
0

Esta consulta debería darle un resultado equivalente a trunc(sysdate)en Oracle.

SELECT  * 
FROM    your_table
WHERE   CONVERT(varchar(12), your_column_name, 101)
      = CONVERT(varchar(12), GETDATE(), 101)

¡Espero que esto ayude!

Sandeep Gaadhe
fuente
0

También puede extraer la fecha using Substringde la variable de fecha y hora y volver a la fecha y hora ignorará parte del tiempo.

declare @SomeDate datetime = '2009-05-28 16:30:22'
SELECT cast(substring(convert(varchar(12),@SomeDate,111),0,12) as Datetime) 

Además, puede acceder a partes de la variable de fecha y hora y combinarlas en una fecha truncada de construcción, algo como esto:

SELECT cast(DATENAME(year, @Somedate) + '-' + 
       Convert(varchar(2),DATEPART(month, @Somedate)) + '-' +
       DATENAME(day, @Somedate) 
       as datetime)
Nunca sin esperanza
fuente
0

Oráculo:

TRUNC(SYSDATE, 'MONTH')

Servidor SQL:

DATEADD(DAY, - DATEPART(DAY, DateField) + 1, DateField)

Podría usarse de manera similar para truncar minutos u horas de una fecha.

Markus
fuente
0

podrías hacer esto (SQL 2008):

declarar @SomeDate date = getdate ()

select @SomeDate

2009-05-28

Hagai Danenberg-Lerner
fuente