¿Cómo puedo agrupar el tiempo por horas o por 10 minutos?
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 % 10
como / 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?
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)
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 MINUTE
y 10
se pueden cambiar a cualquiera DATEPART
y entero, 2 respectivamente, para agruparlos en diferentes intervalos de tiempo. Por ejemplo, 10
con MINUTE
intervalos de diez minutos; 6
con HOUR
intervalos 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 BY
expresión por el tamaño del intervalo y envolverla en una DATEADD
invocación le devolverá un DATETIME
valor. Incluirlo en la SELECT
declaració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 DATEDIFF
trunca los valores a números enteros (un FLOOR
atajo), lo que produce el comienzo de los intervalos de tiempo para cada fila en su SELECT
salida.
Si desea etiquetar cada fila con la mitad o el final de su intervalo, puede modificar la división en el segundo término DATEADD
con 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 BY
para 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 0
el 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 BY
segundos 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:00
las marcas de tiempo sean elegibles para la agrupación, use un número entero en el que suDATEPART
máximo pueda dividirse uniformemente. 5 Como contraejemplo, agrupar los resultados en intervalos de 13 minutos o 37 horas omitirá algunos:00
mensajes, pero aún así debería funcionar bien .
3 Las matemáticas dicen 2 32 ≈ 4.29E+9. Esto significa que para unDATEPART
deSECOND
, 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.
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)