2008-09-17 19 views
63

En SQL Server, ¿cómo "piso" un DATETIME al segundo/minuto/hora/día/año?Poner al día una fecha en el servidor SQL

Digamos que tengo una fecha de 2008-09-17 12: 56: 53.430, entonces la salida del suelo debe ser:

  • Año: 2008-01-01 00:00: 00.000
  • Mes: 2008-09-01 00: 00: 00.000
  • Día: 2008-09-17 00: 00: 00.000
  • horas: 2008-09-17 12: 00: 00.000
  • Minuto : 2008-09-17 12: 56: 00,000
  • Segundo: 2008-09-17 12: 56: 53,000

Respuesta

91

La clave es usar DATEADD y DATEDIFF junto con la enumeración intervalo de tiempo SQL apropiado.

declare @datetime datetime; 
set @datetime = getdate(); 
select @datetime; 
select dateadd(year,datediff(year,0,@datetime),0); 
select dateadd(month,datediff(month,0,@datetime),0); 
select dateadd(day,datediff(day,0,@datetime),0); 
select dateadd(hour,datediff(hour,0,@datetime),0); 
select dateadd(minute,datediff(minute,0,@datetime),0); 
select dateadd(second,datediff(second,'2000-01-01',@datetime),'2000-01-01'); 
select dateadd(week,datediff(week,0,@datetime),-1); --Beginning of week is Sunday 
select dateadd(week,datediff(week,0,@datetime),0); --Beginning of week is Monday 

Tenga en cuenta que cuando se está suelo por el segundo, que a menudo se obtiene un desbordamiento aritmético si utiliza 0. Así que elige un valor conocido que se garantiza que sea más baja que la fecha y hora que está tratando de suelo.

+1

La fecha a calcular su desplazamiento desde no tiene por qué ser en el pasado. Cualquier fecha funcionará, siempre que esté "PISADA" al intervalo en preguntas. Si la fecha base está en el futuro, solo obtiene un valor de compensación negativo ... – MatBailie

+0

Para usar la palabra piso a la semana, use esto si el domingo es el primer día de la semana ... seleccione dateadd (week, datediff (week, 0, @datetime), - 1) –

+0

Use esto si el lunes es el primer día de la semana ... seleccione dateadd (week, datediff (week, 0, @ datetime), 0) –

2

La función CONVERT() puede hacer esto también, dependiendo del estilo que use.

+2

Hemos encontrado que CONVERT() puede estar en cualquier lugar del 10% al 5x menos rendimiento que dateadd/datediff. SQL impone una penalización para convertir entre tipos numéricos y cadenas y luego de vuelta. – Portman

1

Lástima que no es Oracle, o podría usar trunc() o to_char().

Pero tenía problemas similares con SQL Server y utilizado el CONVERT() y() DateDiff métodos, como se indica here

28

En SQL Server aquí hay un pequeño truco para hacer eso:

SELECT CAST(FLOOR(CAST(CURRENT_TIMESTAMP AS float)) AS DATETIME) 

Usted echa el DateTime en un flotante, que representa la fecha como la parte entera y el tiempo como la fracción de un día que pasó. Corta esa porción decimal, luego regresa a DateTime, y tienes la medianoche al comienzo de ese día.

Esto es probablemente más eficiente que todas las cosas DATEADD y DATEDIFF. Ciertamente es mucho más fácil escribir.

+1

En realidad, eso es un 25% más de caracteres que dateadd (día, fechada (día, 0, @ fecha y hora), 0), por lo que no es más fácil escribir. También es un 15% menos eficiente. – Portman

+9

@Portman: ¿hay alguna base para su reclamo que sea un 15% menos eficiente? – Hogan

+3

casting to floor perjudica el rendimiento ya que saltará cualquier índice de fecha y hora. Cuando se usa SQL 2008 es mejor usar las funciones de fechada o CAST ([campo] COMO TIEMPO) o CAST ([campo] como FECHA) – Rik

-2

Desde PostgreSQL es también un "SQL Server", voy a mencionar

date_trunc() 

que hace exactamente lo que estás pidiendo gracia.

Por ejemplo:

 
select date_trunc('hour',current_timestamp); 
     date_trunc 
------------------------ 
2009-02-18 07:00:00-08 
(1 row) 

+0

"Servidor SQL" aquí se refiere al SQL DBMS de Microsoft. El nombre es bastante confuso de hecho. –

10

Ampliando la solución Convert/moldeada, en Microsoft SQL Server 2008, puede hacer lo siguiente:

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

basta con sustituir getdate() con cualquier columna que es una fecha y hora .

No hay cadenas involucradas en esta conversión.

Esto está bien para consultas ad-hoc o actualizaciones, pero para combinaciones de clave o procesamiento muy usado puede ser mejor manejar la conversión dentro del procesamiento o redefinir las tablas para tener claves y datos apropiados.

En 2005, puede utilizar el piso más desordenado: cast(floor(cast(getdate() as float)) as datetime)

No creo que utiliza conversión de cadenas tampoco, pero no se puede hablar de la comparación de la eficiencia real en comparación con las estimaciones de sillón.

6

He utilizado @Portman's answer muchas veces a lo largo de los años como referencia cuando las fechas del piso y han trasladado su funcionamiento a una función que puede serle útil.

No reclamo su rendimiento y simplemente lo proporciono como una herramienta para el usuario.

Le pido que, si decide votar esta respuesta, también devuelva @Portman's answer, ya que mi código es un derivado de la suya.

IF OBJECT_ID('fn_FloorDate') IS NOT NULL DROP FUNCTION fn_FloorDate 
SET ANSI_NULLS OFF 
GO 
SET QUOTED_IDENTIFIER ON 
GO 
CREATE FUNCTION [dbo].[fn_FloorDate] (
    @Date DATETIME = NULL, 
    @DatePart VARCHAR(6) = 'day' 
) 
RETURNS DATETIME 
AS 
BEGIN 
    IF (@Date IS NULL) 
    SET @Date = GETDATE(); 

    RETURN 
    CASE 
    WHEN LOWER(@DatePart) = 'year' THEN DATEADD(YEAR, DATEDIFF(YEAR, 0, @Date), 0) 
    WHEN LOWER(@DatePart) = 'month' THEN DATEADD(MONTH, DATEDIFF(MONTH, 0, @Date), 0) 
    WHEN LOWER(@DatePart) = 'day' THEN DATEADD(DAY, DATEDIFF(DAY, 0, @Date), 0) 
    WHEN LOWER(@DatePart) = 'hour' THEN DATEADD(HOUR, DATEDIFF(HOUR, 0, @Date), 0) 
    WHEN LOWER(@DatePart) = 'minute' THEN DATEADD(MINUTE, DATEDIFF(MINUTE, 0, @Date), 0) 
    WHEN LOWER(@DatePart) = 'second' THEN DATEADD(SECOND, DATEDIFF(SECOND, '2000-01-01', @Date), '2000-01-01') 
    ELSE DATEADD(DAY, DATEDIFF(DAY, 0, @Date), 0) 
    END; 
END 

Uso:

DECLARE @date DATETIME; 
SET @date = '2008-09-17 12:56:53.430'; 

SELECT 
    @date AS [Now],--2008-09-17 12:56:53.430 
    dbo.fn_FloorDate(@date, 'year') AS [Year],--2008-01-01 00:00:00.000 
    dbo.fn_FloorDate(default, default) AS [NoParams],--2013-11-05 00:00:00.000 
    dbo.fn_FloorDate(@date, default) AS [ShouldBeDay],--2008-09-17 00:00:00.000 
    dbo.fn_FloorDate(@date, 'month') AS [Month],--2008-09-01 00:00:00.000 
    dbo.fn_FloorDate(@date, 'day') AS [Day],--2008-09-17 00:00:00.000 
    dbo.fn_FloorDate(@date, 'hour') AS [Hour],--2008-09-17 12:00:00.000 
    dbo.fn_FloorDate(@date, 'minute') AS [Minute],--2008-09-17 12:56:00.000 
    dbo.fn_FloorDate(@date, 'second') AS [Second];--2008-09-17 12:56:53.000 
0

Hay varias maneras de pelar este gato =)

select convert(datetime,convert(varchar,CURRENT_TIMESTAMP,101)) 
+0

Una respuesta anterior sugirió esto y se comentó (más lento) – Hogan

0

DateAdd junto con DifFecha puede ayudar a hacer muchas tareas diferentes. Por ejemplo, puede encontrar el último día de cualquier mes y puede encontrar el último día del mes anterior o el siguiente.

----Last Day of Previous Month 
SELECT DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,GETDATE()),0)) 
LastDay_PreviousMonth 
----Last Day of Current Month 
SELECT DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,GETDATE())+1,0)) 
LastDay_CurrentMonth 
----Last Day of Next Month 
SELECT DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,GETDATE())+2,0)) 
LastDay_NextMonth 

Source

Cuestiones relacionadas