2010-04-14 14 views
11

Si tengo un valor de fecha como 2010-03-01 17:34:12.018¿Cuál es la mejor manera de truncar una fecha en SQL Server?

¿Cuál es la forma más eficiente de convertir esto en 2010-03-01 00:00:00.000?

Como una pregunta secundaria, ¿cuál es la mejor manera de emular la función TRUNC de Oracle, que le permitirá truncar en los límites de año, trimestre, mes, semana, día, hora, minuto y segundo?

+0

Aquí hay una pregunta similar con una buena explicación: http://stackoverflow.com/questions/923295/how-to-truncate-a-datetime-in-sql-server – Alex

Respuesta

25

Para redondear a todo el día, existen tres enfoques de amplio uso. El primero usa datediff para encontrar el número de días desde el 0 datetime. La fecha de 0 corresponde al 1 de enero de 1900. Al agregar la diferencia de días a la fecha de inicio, se redondea a un día completo;

select dateadd(d, 0, datediff(d, 0, getdate())) 

El segundo método se basa el texto: se trunca el texto de descripción con varchar(10), dejando sólo la parte de fecha:

select convert(varchar(10),getdate(),111) 

El tercer método utiliza el hecho de que un datetime es realmente un punto flotante que representa el número de días transcurridos desde el año 1900. por lo tanto redondeando a un número entero, por ejemplo usando floor, se obtiene el inicio de la jornada:

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

Para responder a su segunda pregunta, el inicio de la semana es más complicado. Una forma es restar el día-de-semana:

select dateadd(dd, 1 - datepart(dw, getdate()), getdate()) 

Esto devuelve una parte demasiado tiempo, por lo que lo tienes que combinarlo con uno de los métodos tiempo-stripping para llegar a la primera fecha . Por ejemplo, con @start_of_day como una variable para facilitar la lectura:

declare @start_of_day datetime 
set @start_of_day = cast(floor(cast(getdate() as float)) as datetime) 
select dateadd(dd, 1 - datepart(dw, @start_of_day), @start_of_day) 

El comienzo del año, mes, hora y minuto todavía trabajan con la "diferencia desde 1900" enfoque:

select dateadd(yy, datediff(yy, 0, getdate()), 0) 
select dateadd(m, datediff(m, 0, getdate()), 0) 
select dateadd(hh, datediff(hh, 0, getdate()), 0) 
select dateadd(mi, datediff(mi, 0, getdate()), 0) 

Redondeo por segundo requiere un enfoque diferente, ya que el número de segundos desde 0 da un desbordamiento. Una forma de evitar que se utiliza el inicio de la jornada, en lugar de 1900, como fecha de referencia:

declare @start_of_day datetime 
set @start_of_day = cast(floor(cast(getdate() as float)) as datetime) 
select dateadd(s, datediff(s, @start_of_day, getdate()), @start_of_day) 

Para ronda por 5 minutos, ajustar el método de redondeo minutos. Tome el cociente de la diferencia de minutos, por ejemplo usando /5*5:

select dateadd(mi, datediff(mi,0,getdate())/5*5, 0) 

Esto funciona para los cuartos y medias horas también.

+0

¿Cuál va a funcionar mejor? –

+0

Por lo que sé, la diferencia en el rendimiento es demasiado pequeña para ser medida – Andomar

+0

¡Eso es lo que también quiero saber! Utilizo el segundo en mi código, pero tenía curiosidad de saber si había una forma más rápida de hacerlo. La primera son dos funciones de fecha con una tercera llamada de función, por lo que hay al menos tres operaciones de análisis, mientras que la segunda es un solo molde a cadena con un formato aplicado (pero ahí radica el problema, ¿qué ocurre en las bibliotecas?) – jcolebrand

2

Probar:

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

ACTUALIZACIÓN: respuesta a la segunda pregunta: durante años se podría utilizar un poco de bit versión modificada de mi respuesta:

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

por trimestre:

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

y así sucesivamente.

Lo revisé, hasta minutos - está bien. Pero en segundo Tengo un mensaje de desbordamiento:

diferencia de dos columnas de fecha y hora causaron el desbordamiento en tiempo de ejecución.

Uno más actualizado: echar un vistazo a la siguiente respuesta a la misma question

15

Si está utilizando SQL Server 2008, puede utilizar la nueva Date tipo de datos de esta manera:

select cast(getdate() as date) 

Si aún necesita que su valor sea un tipo de datos DateTime, puede hacer esto:

select cast(cast(getdate() as date) as datetime) 

Un método que debe funcionar en todas las versiones de SQL Server es:

select cast(floor(cast(getdate() as float)) as datetime) 
+0

Apuesto a que este es el más rápido, evitando la aritmética en conjunto. –

1

Ésta es tarde, pero producirá los resultados exactos solicitados en el puesto. También creo que es mucho más intuitivo que usar dateadd, pero esa es mi preferencia.

declare @SomeDate datetime = '2010-03-01 17:34:12.018' 
SELECT 
DATEFROMPARTS(
    YEAR(@SomeDate) 
    ,MONTH(@SomeDate) 
    ,'01' 
    ) AS CUR_DATE_FROM_PARTS 
,DATETIMEFROMPARTS(
    YEAR(@SomeDate)      
    ,MONTH(@SomeDate)     
    ,'01' --DAY(@SomeDate)     
    ,'00' --DATEPART(HOUR,@SomeDate)   
    ,'00' --DATEPART(MINUTE,@SomeDate)  
    ,'00' --DATEPART(SECOND,@SomeDate)  
    ,'00' --DATEPART(MILLISECOND,@SomeDate) 
    ) AS CUR_DATETIME_FROM_PARTS 
,@SomeDate       AS CUR_DATETIME 
,YEAR(@SomeDate)     AS CUR_YEAR 
,MONTH(@SomeDate)     AS CUR_MONTH 
,DAY(@SomeDate)     AS CUR_DAY 
,DATEPART(HOUR,@SomeDate)   AS CUR_HOUR 
,DATEPART(MINUTE,@SomeDate)  AS CUR_MINUTE 
,DATEPART(SECOND,@SomeDate)  AS CUR_SECOND 
,DATEPART(MILLISECOND,@SomeDate) AS CUR_MILLISECOND 
FROM Your_Table 

truncado Fecha: 2010-03-01

truncado DateTime: 2010-03-01 00: 00: 00,000

DateTime: 2010-03-01 17: 34: 12.017

Cuestiones relacionadas