2010-07-20 34 views
5

Tengo una tabla con marcas de tiempo, y quiero dividir esta tabla en intervalos de una hora, comenzando de ahora en adelante hacia atrás un par de horas. No puedo obtener los resultados que necesito con la función T-SQL DATEDIFF, ya que cuenta el número de veces que la manecilla de minutos pasa 12 entre las dos fechas. Quiero el número de veces que la manecilla de minutos pase donde está ahora entre la marca de tiempo y ahora.T-SQL DateDiff - Partición por "horas completas", en lugar de "minutos multiplicados por 00 desde"

¿Hay una manera directa de hacer esto en T-SQL?

Actualización: En respuesta a los comentarios, he aquí algunos datos de ejemplo, la consulta actualmente estoy usando y los resultados que estoy obteniendo, así como los resultados que quiero.

datos de la muestra:

TimeStamp 
********* 
2010-07-20 11:00:00.000 
2010-07-20 10:44:00.000 
2010-07-20 10:14:00.000 
2010-07-20 11:00:00.000 
2010-07-20 11:40:00.000 
2010-07-20 10:16:00.000 
2010-07-20 13:00:00.000 
2010-07-20 12:58:00.000 

consulta actual:

SELECT TimeStamp, DATEDIFF(HOUR, TimeStamp, CURRENT_TIMESTAMP) AS Diff FROM ... 

Resultados:

 TimeStamp     Diff 
    *********     **** 
    2010-07-20 11:00:00.000  2 
    2010-07-20 10:44:00.000  3 
    2010-07-20 10:14:00.000  3 
    2010-07-20 11:00:00.000  2 
    2010-07-20 11:40:00.000  2 
    2010-07-20 10:16:00.000  3 
    2010-07-20 13:00:00.000  0 
    2010-07-20 12:58:00.000  1

Lo que yo prefiero:

 -- The time is now, for the sake of the example, 13:40 

    TimeStamp     Diff 
    *********     **** 
    2010-07-20 11:00:00.000  3 -- +1 
    2010-07-20 10:44:00.000  3 
    2010-07-20 10:14:00.000  4 -- +1 
    2010-07-20 11:00:00.000  3 -- +1 
    2010-07-20 11:40:00.000  2 or 3 -- edge case, I don't really care which 
    2010-07-20 10:16:00.000  4 -- +1 
    2010-07-20 13:00:00.000  1 -- +1 
    2010-07-20 12:58:00.000  1

He marcado los resultados que cambiaron con un +1. Además, no me importa si se trata de un índice 0 o 1-indexado, pero básicamente, si es ahora 13:40 Quiero que los lapsos de tiempo que obtienen el mismo valor para ser

 12:40-13:40 1 (or 0) 
    11:40-12:40 2 (or 1) 
    10:40-11:40 3 (or 2) 
    09:40-10:40 4 (or 3)
+0

se explica que con los datos de entrada y salida esperada con la pregunta, ¿has escrito? – shahkalpesh

+0

DATEDIFF en minutos y luego la división entera por 60? –

Respuesta

7

no puedes simplemente use DATEDIFF(minute,.. y luego divida el resultado entre 60 y tome el valor entero. p.ej.

SELECT DATEDIFF(minute, '2010-07-20 06:00', GETDATE())/60 

Creo que esto se convierte implícitamente como un int como datediff devuelve un int, da horas enteras sin ninguna aproximación.

Para utilizar su consulta exacta de su posterior actualización:

SELECT TimeStamp, (DATEDIFF(minute, TimeStamp, CURRENT_TIMESTAMP) /60) AS Diff FROM ... 
+0

Esto aún no será preciso. Acaba de cambiar el error de 59 minutos a 59 segundos ... – ErikE

+0

@Emtucifor: un error de 59 segundos es aún mucho mejor. Voy a usar esto para decidir si un usuario debe recibir una notificación o no, y solo deberían obtener como máximo una por hora. Un error de 59 minutos en ese caso es catastrófico; un error de 59 segundos es bastante bueno. E incluso puedo conseguir que el error sea aún más pequeño (<1 segundo) haciendo lo mismo con los segundos que hace Ben con los minutos. –

+0

Bien, no hay problema entonces. Sin embargo, me gusta más mi versión de restar fechas, porque es aún más simple y es tan exacta como permite el tipo de datos. :) – ErikE

0

Se pueden agrupar en esto:

SELECT DateDiff(Hour, 0, GetDate() - TimeStamp) 

Si desea saber el tiempo que esto representa, calc de nuevo:

DateAdd(Hour, -DateDiff(Hour, 0, GetDate() - TimeStamp), GetDate()) 

Si no le gusta restar fechas, aún se puede hacer, pero se vuelve un poco más difícil. En lugar de simplemente disparar en la oscuridad, hice una consulta para probar que esto es correcto.

SELECT 
    TimeStamp, 
    Now = GetDate(), 
    HourDiff = DateDiff(Hour, 0, GetDate() - TimeStamp), 
    HourCalc = DateAdd(Hour, -DateDiff(Hour, 0, GetDate() - TimeStamp), GetDate()), 
    HourDiff2 = DateDiff(Hour, DateAdd(Millisecond, AdjustMs, TimeStamp), DateAdd(Millisecond, AdjustMs, GetDate())), 
    HourCalc2 = DateAdd(Hour, -DateDiff(Hour, DateAdd(Millisecond, AdjustMs, TimeStamp), DateAdd(Millisecond, AdjustMs, GetDate())), GetDate()) 
FROM 
    (
     SELECT DateAdd(Second, -3559, GetDate()) 
     UNION ALL SELECT DateAdd(Second, -3600, GetDate()) 
     UNION ALL SELECT DateAdd(Second, -3601, GetDate()) 
    ) x (TimeStamp) 
    CROSS JOIN (
     SELECT 3599997 - DateDiff(Millisecond, 0, DateAdd(Hour, -DateDiff(Hour, 0, GetDate()), GetDate())) 
    ) D (AdjustMs) 

Por desgracia, tuve que explotar mi conocimiento de la resolución del tipo de datos de fecha y hora (1/300th de un segundo), por lo tanto 3600000 - 3 = 3599997. Si el ajuste de milisegundos se calculó en base a la marca de tiempo en lugar de GetDate () entonces esto no sería necesario, pero sería mucho más desordenado ya que la gran expresión dentro de la tabla derivada D tendría que ser utilizada dos veces en la consulta principal, reemplazando AdjustMs.

Los cálculos son más complicados de lo que podría parecer necesario porque no puede simplemente calcular la diferencia de milisegundos entre las fechas aleatorias o obtendrá un error de desbordamiento. Si conoce los rangos de fechas posibles, puede realizar cálculos directos de milisegundos usando una fecha de anclaje diferente a '19000101 00: 00: 00,000' (el 0 en las expresiones anteriores).

Pensándolo bien, que sólo recibe 24+ días de milisegundos en una larga firmado:

SELECT DateAdd(Millisecond, 2147483647, 0) = '1900-01-25 20:31:23.647' 
+0

Eso parece recorrer un largo camino para evitar restar fechas, pero de su publicación lo considero que podría haber buenas razones para no querer hacer eso. ¿Por qué no quiero restar fechas? –

+0

Bueno, no es un requisito en este caso con ** datetime **, pero en SQL Server 2008 no puede agregar o restar valores del tipo de datos ** fecha **. Aunque hacerlo con ** datetime ** funciona por ahora, puede que no siempre. C# .Net también prohíbe esto (solo puede agregar y restar timespans de fechas u otros timespans) por lo que todos deberíamos estar pensando en formas de evitarlo si vamos a estar al tanto de esto a largo plazo. ¡Algún día restar fechas de tiempo puede ser desaprobado y luego eliminado como una función! – ErikE

0

que haría uso de

FLOOR(24 * CAST(CURRENT_TIMESTAMP-[TimeStamp] as float)) 

caso de prueba

DECLARE @GetDate datetime 
set @GetDate = '2010-07-20 13:40:00.000'; 

WITH TestData As 
(
select CAST('2010-07-20 11:00:00.000' AS DATETIME) AS [TimeStamp] UNION ALL 
select '2010-07-20 10:44:00.000' UNION ALL  
select '2010-07-20 10:14:00.000' UNION ALL 
select '2010-07-20 11:00:00.000' UNION ALL 
select '2010-07-20 11:40:00.000' UNION ALL 
select '2010-07-20 10:16:00.000' UNION ALL 
select '2010-07-20 13:00:00.000' UNION ALL 
select '2010-07-20 12:58:00.000' 
) 

SELECT [TimeStamp], FLOOR(24 * CAST(@GetDate-[TimeStamp] as float)) AS Diff 
FROM TestData 

Resultados

(Usted tendría que añadir al menos 1 para obtener los resultados exactos que posteaste pero decir que no te molesta 0 o 1 indexados)

TimeStamp    Diff 
----------------------- ---------------------- 
2010-07-20 11:00:00.000 2 
2010-07-20 10:44:00.000 2 
2010-07-20 10:14:00.000 3 
2010-07-20 11:00:00.000 2 
2010-07-20 11:40:00.000 2 
2010-07-20 10:16:00.000 3 
2010-07-20 13:00:00.000 0 
2010-07-20 12:58:00.000 0 
+0

¿Por qué el '24 *'? ¿Qué me dice 'CAST (@ GetDate- [TimeStamp] AS FLOAT)'? –

+0

@Tomas. 'CAST (@ GetDate- [TimeStamp] AS FLOAT)' es el número de días de diferencia. p.ej. 1.75 sería 1 día y 18 horas de diferencia. Entonces multiplicar por 24 da la cantidad de horas de diferencia. Usar 'Floor' luego agrupa todo en una hora juntos. –

+0

Martin, en mi experiencia convirtiendo a float no es confiable para datetime. Lo estudié atentamente en el pasado. Datediff (Hora, 0, Current_TimeStamp - TimeStamp) hace el mismo truco. – ErikE

Cuestiones relacionadas