¿Cómo puedo agrupar el tiempo por horas o por 10 minutos?

Resuelto cnd asked hace 13 años • 18 respuestas

como cuando lo hago

SELECT [Date]
  FROM [FRIIB].[dbo].[ArchiveAnalog]
  GROUP BY [Date]

¿Cómo puedo especificar el período del grupo? Estoy usando MS SQL 2008.

Probé esto, tanto con % 10como / 10.

SELECT MIN([Date]) AS RecT, AVG(Value)
  FROM [FRIIB].[dbo].[ArchiveAnalog]
  GROUP BY (DATEPART(MINUTE, [Date]) / 10)
  ORDER BY RecT

¿Es posible generar una salida de fecha sin milisegundos?

cnd avatar Feb 15 '11 17:02 cnd
Aceptado

finalmente hecho con

GROUP BY
DATEPART(YEAR, DT.[Date]),
DATEPART(MONTH, DT.[Date]),
DATEPART(DAY, DT.[Date]),
DATEPART(HOUR, DT.[Date]),
(DATEPART(MINUTE, DT.[Date]) / 10)
cnd avatar Feb 24 '2011 07:02 cnd

Corto y dulce

GROUP BY DATEDIFF(MINUTE, '2000', date_column) / 10

Con un gran agradecimiento a la respuesta de Derek , que forma el núcleo de esta. Si tiene SQL Server 2022+, vaya directamente a la respuesta de Martin .

Uso práctico

SELECT   DATEADD(MINUTE, DATEDIFF(MINUTE, '2000', aa.[date]) / 10 * 10, '2000')
                                                             AS [date_truncated],
         COUNT(*) AS [records_in_interval],
         AVG(aa.[value]) AS [average_value]
FROM     [friib].[dbo].[archive_analog] AS aa
-- WHERE aa.[date] > '1900-01-01'
GROUP BY DATEDIFF(MINUTE, '2000', aa.[date]) / 10
-- HAVING SUM(aa.[value]) > 1000
ORDER BY [date_truncated]

Detalles y comentarios adicionales

Tamaño del intervalo del contenedor

Los términos MINUTEy 10se pueden cambiar a cualquiera DATEPARTy entero, 2 respectivamente, para agruparlos en diferentes intervalos de tiempo. Por ejemplo, 10con MINUTEintervalos de diez minutos; 6con HOURintervalos de seis horas.

Si cambia mucho el intervalo, podría beneficiarse de declararlo como una variable.

DECLARE @interval int = 10;

SELECT   DATEADD(MINUTE, DATEDIFF(…) / @interval * @interval, '2000')
…
GROUP BY                 DATEDIFF(…) / @interval

Tipo de valor

Los valores reales que se agrupan son un conjunto de compensaciones relativas desde 2000-01-01 00:00. Esto significa que las fuentes de datos en intervalos de tiempo prolongados están bien. Algunas otras respuestas tienen colisión entre años.

Multiplicar la GROUP BYexpresión por el tamaño del intervalo y envolverla en una DATEADDinvocación le devolverá un DATETIMEvalor. Incluirlo en la SELECTdeclaración le dará a su salida una sola columna con la marca de tiempo truncada. Consulte el ejemplo de "Uso práctico" anterior.

Cambiar la etiqueta para el intervalo del contenedor

La /operación de división ( ) después DATEDIFFtrunca los valores a números enteros (un FLOORatajo), lo que produce el comienzo de los intervalos de tiempo para cada fila en su SELECTsalida.

Si desea etiquetar cada fila con la mitad o el final de su intervalo, puede modificar la división en el segundo término DATEADDcon la parte en negrita a continuación:

  • Fin del intervalo: crédito a Daniel Elkington .…) / 10 * 10 + 10 , '2000')
  • Mitad del intervalo: .…) / 10 * 10 + (10 / 2.0) , '2000')

Si desea redondear sus intervalos hacia adentro de modo que cada marca de tiempo represente la mitad de un intervalo antes y la mitad de un intervalo después, use algo como esto:

DATEADD(MINUTE, ROUND(1. * DATEDIFF(MINUTE, '2000', date_column) / 10, 0) * 10, '2000')

Tenga en cuenta que 1.en su lugar se debe realizar una división no truncada. Deberá modificar su GROUP BYpara que coincida y es posible que desee utilizar la ROUND(…)expresión completa para evitar cualquier redondeo flotante inesperado.

Trivia de matemáticas sobre citas

'2000'es una "fecha de anclaje" alrededor de la cual SQL realizará los cálculos de fechas. La mayoría del código de muestra utiliza 0el ancla, pero JereonH descubrió que se produce un desbordamiento de enteros al agrupar fechas más recientes por segundos o milisegundos. 3

Si sus datos abarcan siglos, 4 el uso de una única fecha de anclaje en GROUP BYsegundos o milisegundos seguirá encontrando el desbordamiento. Para esas consultas, puede pedir a cada fila que fije la comparación de agrupación en la medianoche de su propia fecha.

Utilice uno de los dos reemplazos en lugar de '2000'donde aparece en la consulta:

  • DATEADD(DAY, DATEDIFF(DAY, 0, aa.[date]), 0)
  • CONVERT(DATETIME, CONVERT(DATE, aa.[date]))

Su consulta será totalmente ilegible, pero funcionará.


1 Varios años después de publicar, me di cuenta de que mi código podría simplificarse hasta casi ser igual a la respuesta de Derek .
2 Si desea que todas:00las marcas de tiempo sean elegibles para la agrupación, use un número entero en el que suDATEPARTmáximo pueda dividirse uniformemente. 5 Como contraejemplo, agrupar los resultados en intervalos de 13 minutos o 37 horas omitirá algunos:00mensajes, pero aún así debería funcionar bien .
3 Las matemáticas dicen 2 32 ≈ 4.29E+9. Esto significa que para unDATEPARTdeSECOND, obtienes 4,3 mil millones de segundos en cada lado, lo que equivale a una "fecha de anclaje ± 136 años". De manera similar, 2 32 milisegundos son ≈ 49,7 días.
4 Si sus datos realmente abarcan siglos o milenios y aún son precisos al segundo o al milisegundo... ¡felicidades! Hagas lo que hagas, sigue haciéndolo.
5 Si alguna vez te preguntaste por qué nuestros relojes tienen un 12 en la parte superior, reflexiona sobre cómo 5 es el único número entero de 6 (la mitad de 12) o menos que no es un factor de 12. Luego, observa que 5 × 12 = 60. Tiene muchas opciones para tamaños de contenedores con horas, minutos y segundos.

Michael come lately avatar Jan 30 '2017 19:01 Michael come lately

En T-SQL puedes:

SELECT [Date]
  FROM [FRIIB].[dbo].[ArchiveAnalog]
  GROUP BY [Date], DATEPART(hh, [Date])

o

por minuto de usoDATEPART(mi, [Date])

o

por 10 minutos de uso DATEPART(mi, [Date]) / 10(como sugirió Timothy)

tzup avatar Feb 15 '2011 10:02 tzup