Cómo agrupar el tiempo por hora o por 10 minutos


167

como cuando lo hago

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

¿Cómo puedo especificar el período del grupo?

MS SQL 2008

2da Edición

Lo estoy intentando

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

cambió% 10 a / 10. ¿es posible hacer una salida de Fecha sin milisegundos?

Respuestas:


213

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)

11
Lo hice ROUND((DATEPART(MINUTE, DT.[Date]) / 5),0,1) * 5, de modo que cuando miro los datos se correlaciona con el intervalo de tiempo más cercano
hdost

77
Año, mes y día se pueden simplificar a DATE(DT.[Date]).

@Keelan no funciona para mí, sin embargo CONVERT (fecha, DT. [Fecha]) sí.
Dan Parsonson

datepart(hour, workingPolicy.workingHours)/2.0da 1.5mientras datepart(hour, '1900-01-01 09:00:30.000')/2.0da 4.5, no entiendo por qué? Nota: workingPolicy.workingHours = 1900-01-01 09: 00: 30.000 . por favor ayuda
affanBajwa

65

Llego muy tarde a la fiesta, pero esto no aparece en ninguna de las respuestas existentes:

GROUP BY DATEADD(MINUTE, DATEDIFF(MINUTE, '2000', date_column) / 10 * 10, '2000')
  • Los términos 10y MINUTEse pueden cambiar a cualquier número y DATEPART, respectivamente.
  • Es un DATETIMEvalor, lo que significa:
    • Funciona bien en largos intervalos de tiempo. (No hay colisión entre años).
    • Incluirlo en la SELECTdeclaración le dará a su salida una columna con una salida bonita truncada en el nivel que especifique.
  • '2000'es una "fecha de anclaje" alrededor de la cual SQL realizará la matemática de fecha. Jereonh descubrió a continuación que encuentra un desbordamiento de enteros con el ancla anterior ( 0) cuando agrupa fechas recientes por segundos o milisegundos.
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
GROUP BY DATEADD(MINUTE, DATEDIFF(MINUTE, '2000', aa.[date]) / 10 * 10, '2000')
ORDER BY [date_truncated]

Si sus datos abarcan siglos, ‡ el uso de una sola fecha de anclaje para la agrupación de segundos o milisegundos seguirá encontrando el desbordamiento. Si eso sucede, puede pedirle a cada fila que ancle la comparación de agrupación a la medianoche de su propia fecha:

  • Use en DATEADD(DAY, DATEDIFF(DAY, 0, aa.[date]), 0)lugar de '2000'donde aparezca arriba. Su consulta será totalmente ilegible, pero funcionará.

  • Una alternativa podría ser CONVERT(DATETIME, CONVERT(DATE, aa.[date]))el reemplazo.

2 32 ≈ 4.29E + 9, por lo que si DATEPARTes así SECOND, obtendrá 4.300 millones de segundos a cada lado o "ancla ± 136 años". Del mismo modo, 2 32 milisegundos es ≈ 49.7 días.
Si sus datos realmente abarcan siglos o milenios y aún son precisos al segundo o milisegundo ... ¡felicidades! Lo que sea que estés haciendo, sigue haciéndolo.


¿Sería posible hacer esto desde una fecha de inicio específica?
Pylander

1
@pylander Claro. Solo ponga una wherecláusula antes del group by.
Michael - ¿Dónde está Clay Shirky?

2
Veo. Entonces, ¿cambiar esto / 20 * 20sería lo mismo que recopilar datos para intervalos de 20 minutos? Lo siento, estoy luchando con las matemáticas en este momento.
Lopsided

2
para SEGUNDO como DATEPART me sale un mensaje de error ( The datediff function resulted in an overflow. The number of dateparts separating two date/time instances is too large. Try to use datediff with a less precise datepart.). Parece que MINUTE es la parte de fecha más pequeña que puede usar con este enfoque.
jeroenh

1
@jeroenh, tienes razón. He agregado una sección para hablar sobre eso. tldr: Cambie 0a '2000'(¡las comillas son importantes!) e intente SECONDnuevamente.
Michael - ¿Dónde está Clay Shirky?

17

En T-SQL puedes:

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

o

por minuto de uso DATEPART(mi, [Date])

o

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


1
GROUP BY [Fecha], DATEPART (hh, [Fecha]) es un desastre, ¿no?
cnd

1
@nCdy Debería estar bien, de lo contrario obtendrá un error "... no válido en la lista de selección porque no está contenido ni en una función agregada ni en la cláusula GROUP BY"
tzup

1
¿Cómo se agrupa por fecha completa y por horas al mismo tiempo? Solo estoy usando Min Date.
cnd

si usa Min (Fecha), por supuesto, puede sacar la Fecha en el Grupo por.
tzup

3
Los minutos divididos por 10 crearán la agrupación por intervalos de 10 minutos, que es lo que se necesita. El módulo 10 no tiene sentido.
Tar

12

Por un intervalo de 10 minutos, usted

GROUP BY (DATEPART(MINUTE, [Date]) / 10)

Como ya lo mencionaron tzup y Pieter888 ... para hacer un intervalo de una hora, solo

GROUP BY DATEPART(HOUR, [Date])

2
pero aquí agrupo los minutos del año 1980 con los minutos del 2011: SI debe preocuparme por eso.
cnd

¿Sería% la matemática correcta? ¿No crearía eso 10 grupos? Por ejemplo: 1% 10 = 9 2% 10 = 8 Ni siquiera sería necesariamente la agrupación cronológica correcta. Creo que solo una división normal sería correcta. A menos que% no sea una división restante en sql.
Steven

12
Los minutos divididos por 10 crearán la agrupación por intervalos de 10 minutos, que es lo que se necesita. El módulo 10 no tiene sentido.
Tar

De acuerdo con el alquitrán. La agrupación no tiene sentido.
MikeMurko

7

Debería ser algo como

select timeslot, count(*)  
from 
    (
    select datepart('hh', date) timeslot
    FROM [FRIIB].[dbo].[ArchiveAnalog]  
    ) 
group by timeslot

(No estoy 100% seguro de la sintaxis, soy más un tipo de Oracle)

En oráculo:

SELECT timeslot, COUNT(*) 
FROM
(  
    SELECT to_char(l_time, 'YYYY-MM-DD hh24') timeslot 
    FROM
    (
        SELECT l_time FROM mytab  
    )  
) GROUP BY timeslot 

6

La respuesta original que el autor dio funciona bastante bien. Solo para extender esta idea, puedes hacer algo como

group by datediff(minute, 0, [Date])/10

lo que le permitirá agrupar por un período más largo de 60 minutos, digamos 720, que es medio día, etc.


1
¿Cuál sería la consulta MySQL para lo anterior?
Biranchi

4

Para MySql:

GROUP BY
DATE(`your_date_field`),
HOUR(`your_date_field`),
FLOOR( MINUTE(`your_date_field`) / 10);

1

Mi solución es usar una función para crear una tabla con los intervalos de fechas y luego unir esta tabla a los datos que quiero agrupar usando el intervalo de fechas en la tabla. El intervalo de fecha se puede seleccionar fácilmente al presentar los datos.

CREATE FUNCTION [dbo].[fn_MinuteIntervals]
    (
      @startDate SMALLDATETIME ,
      @endDate SMALLDATETIME ,
      @interval INT = 1
    )
RETURNS @returnDates TABLE
    (
      [date] SMALLDATETIME PRIMARY KEY NOT NULL
    )
AS
    BEGIN
        DECLARE @counter SMALLDATETIME
        SET @counter = @startDate
        WHILE @counter <= @endDate
            BEGIN
                INSERT INTO @returnDates VALUES ( @counter )
                SET @counter = DATEADD(n, @interval, @counter)
            END
        RETURN
    END

1
declare @interval tinyint
set @interval = 30
select dateadd(minute,(datediff(minute,0,[DateInsert])/@interval)*@interval,0), sum(Value_Transaction)
from Transactions
group by dateadd(minute,(datediff(minute,0,[DateInsert])/@interval)*@interval,0)

8
Bienvenido a stackoverflow. Debe explicar completamente su respuesta y lo que esto agrega a las otras 10 respuestas que ya están en el sitio.
Simon.SA

0

Para SQL Server 2012, aunque creo que funcionaría en SQL Server 2008R2, utilizo el siguiente enfoque para reducir el tiempo al milisegundo:

DATEADD(MILLISECOND, -DATEDIFF(MILLISECOND, CAST(time AS DATE), time) % @msPerSlice, time)

Esto funciona por:

  • Obtener el número de milisegundos entre un punto fijo y un tiempo objetivo:
    @ms = DATEDIFF(MILLISECOND, CAST(time AS DATE), time)
  • Tomando el resto de dividir esos milisegundos en segmentos de tiempo:
    @rms = @ms % @msPerSlice
  • Agregar el negativo de ese resto al tiempo objetivo para obtener el tiempo de corte:
    DATEADD(MILLISECOND, -@rms, time)

Desafortunadamente, como esto se desborda con microsegundos y unidades más pequeñas, los conjuntos de datos más grandes y finos necesitarían usar un punto fijo menos conveniente.

No he comparado rigurosamente esto y no estoy en big data, por lo que su kilometraje puede variar, pero el rendimiento no fue notablemente peor que los otros métodos probados en nuestros equipos y conjuntos de datos, y el pago en conveniencia del desarrollador para el corte arbitrario hace que valga la pena. para nosotros.


0
select from_unixtime( 600 * ( unix_timestamp( [Date] ) % 600 ) ) AS RecT, avg(Value)
from [FRIIB].[dbo].[ArchiveAnalog]
group by RecT
order by RecT;

reemplace los dos 600 por cualquier número de segundos que desee agrupar.

Si necesita esto con frecuencia y la tabla no cambia, como sugiere el nombre Archive, probablemente sería un poco más rápido convertir y almacenar la fecha (y hora) como unimextime en la tabla.


0

Sé que llego tarde al show con este, pero usé esto, un enfoque bastante simple. Esto le permite obtener los cortes de 60 minutos sin problemas de redondeo.

Select 
   CONCAT( 
            Format(endtime,'yyyy-MM-dd_HH:'),  
            LEFT(Format(endtime,'mm'),1),
            '0' 
          ) as [Time-Slice]

0
select dateadd(minute, datediff(minute, 0, Date), 0),
       sum(SnapShotValue)
FROM [FRIIB].[dbo].[ArchiveAnalog]
group by dateadd(minute, datediff(minute, 0, Date), 0)

1
Proporcione algunas explicaciones cuando responda una pregunta.
Shizzen83
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.