Grupo MySQL por intervalos en un rango de fechas
Pregunta
Voy a graficar los datos de flujo de red almacenados en una base de datos MySQL, y necesito una forma eficiente de obtener los puntos de datos relevantes. Los registros se almacenan con la fecha como un int durante segundos desde la época. Me gustaría poder hacer algo como:
Select SUM(bytes) from table where stime > x and stime < Y
group by (10 second intervals)
¿Hay alguna forma de hacer esto? o, ¿sería más rápido manejarlo localmente en python? incluso para una tabla de filas de 500K?
EDIT
Mi error, el tiempo se almacena como un doble sin firmar en lugar de un INT.
Actualmente estoy usando GROUP BY (FLOOR (stime / I))
donde I es el intervalo deseado.
Solución 5
Utilicé sugerencias de ambas respuestas y un compañero de trabajo. El resultado final es el siguiente:
Select FROM_UNIXTIME(stime), bytes
from argusTable_2009_10_22
where stime > (UNIX_TIMESTAMP()-600)
group by floor(stime /10)
También probé la solución de redondeo, pero los resultados fueron inconsistentes.
oportunidad
Otros consejos
Puede hacer esto usando división entera. No estoy seguro del rendimiento.
Deja que sea tu intervalo deseado en segundos.
SELECT SUM(bytes), ((stime - X) DIV I) as interval
FROM table
WHERE (stime > X) and (stime < Y)
GROUP BY interval
Example, let X = 1500 and I = 10
stime = 1503 -> (1503 - 1500) DIV 10 = 0
stime = 1507 -> (1507 - 1500) DIV 10 = 0
stime = 1514 -> (1514 - 1500) DIV 10 = 1
stime = 1523 -> (1523 - 1500) DIV 10 = 2
¿Has probado lo siguiente? Simplemente divida la columna tyiem en 10 y redondee el resultado hacia abajo.
SELECT SUM(bytes)
FROM table
WHERE stime > x
AND stime < Y
GROUP BY ROUND(stime/10, -1)
No sé si la función ROUND () y la agrupación con llamadas a funciones funciona en MySQL, sin embargo, lo anterior es T-SQL.
FLOOR
en grupo falla a veces. a veces agrupa diferentes tiempos como un valor, por ejemplo, cuando divide el valor con 3, pero no hace lo mismo cuando divide con 4, aunque la diferencia entre estos dos valores es mucho mayor que 3 o 4, que debería agrupar como Dos grupos diferentes. Es mejor convertirlo en unsigned after floor que funciona como:
CAST(FLOOR(UNIX_TIMESTAMP(time_field)/I) AS UNSIGNED INT)
El problema:
A veces, GROUP BY FLOOR (UNIX_TIMESTAMP (time_field) / 3)
da menos grupos en comparación con GROUP BY FLOOR (UNIX_TIMESTAMP (time_field) / 4)
que es matemáticamente no debería ' t sea posible.
SELECT sec_to_time(time_to_sec(datefield)- time_to_sec(datefield)%(10)) as intervals,SUM(bytes)
FROM table
WHERE where stime > x and stime < Y
group by intervals
Hice esto hace un tiempo, así que creé alguna función (con servidor SQL, pero supongo que es casi la misma):
Primero, creé una función escalar que me devolvía el ID de una fecha según un intervalo y una parte de la fecha (minuto, hora, día, mes, año):
CREATE FUNCTION [dbo].[GetIDDate]
(
@date datetime,
@part nvarchar(10),
@intervalle int
)
RETURNS int
AS
BEGIN
-- Declare the return variable here
DECLARE @res int
DECLARE @date_base datetime
SET @date_base = convert(datetime,'01/01/1970',103)
set @res = case @part
WHEN 'minute' THEN datediff(minute,@date_base,@date)/@intervalle
WHEN 'hour' THEN datediff(hour,@date_base,@date)/@intervalle
WHEN 'day' THEN datediff(day,@date_base,@date)/@intervalle
WHEN 'month' THEN datediff(month,@date_base,@date)/@intervalle
WHEN 'year' THEN datediff(year,@date_base,@date)/@intervalle
ELSE datediff(minute,@date_base,@date)/@intervalle END
-- Return the result of the function
RETURN @res
END
Luego creé una función de tabla que me devuelve todo el ID entre un intervalo de fechas:
CREATE FUNCTION [dbo].[GetTableDate]
(
-- Add the parameters for the function here
@start_date datetime,
@end_date datetime,
@interval int,
@unite varchar(10)
)
RETURNS @res TABLE (StartDate datetime,TxtStartDate nvarchar(50),EndDate datetime,TxtEndDate nvarchar(50),IdDate int)
AS
begin
declare @current_date datetime
declare @end_date_courante datetime
declare @txt_start_date nvarchar(50)
declare @txt_end_date nvarchar(50)
set @current_date = case @unite
WHEN 'minute' THEN dateadd(minute, datediff(minute,0,@start_date),0)
WHEN 'hour' THEN dateadd(hour, datediff(hour,0,@start_date),0)
WHEN 'day' THEN dateadd(day, datediff(day,0,@start_date),0)
WHEN 'month' THEN dateadd(month, datediff(month,0,@start_date),0)
WHEN 'year' THEN dateadd(year, datediff(year,0,dateadd(year,@interval,@start_date)),0)
ELSE dateadd(minute, datediff(minute,0,@start_date),0) END
while @current_date < @end_date
begin
set @end_date_courante =
case @unite
WHEN 'minute' THEN dateadd(minute, datediff(minute,0,dateadd(minute,@interval,@current_date)),0)
WHEN 'hour' THEN dateadd(hour, datediff(hour,0,dateadd(hour,@interval,@current_date)),0)
WHEN 'day' THEN dateadd(day, datediff(day,0,dateadd(day,@interval,@current_date)),0)
WHEN 'month' THEN dateadd(month, datediff(month,0,dateadd(month,@interval,@current_date)),0)
WHEN 'year' THEN dateadd(year, datediff(year,0,dateadd(year,@interval,@current_date)),0)
ELSE dateadd(minute, datediff(minute,0,dateadd(minute,@interval,@current_date)),0) END
SET @txt_start_date = case @unite
WHEN 'minute' THEN CONVERT(VARCHAR(20), @current_date, 100)
WHEN 'hour' THEN CONVERT(VARCHAR(20), @current_date, 100)
WHEN 'day' THEN REPLACE(CONVERT(VARCHAR(11), @current_date, 106), ' ', '-')
WHEN 'month' THEN REPLACE(RIGHT(CONVERT(VARCHAR(11), @current_date, 106), 8), ' ', '-')
WHEN 'year' THEN CONVERT(VARCHAR(20), datepart(year,@current_date))
ELSE CONVERT(VARCHAR(20), @current_date, 100) END
SET @txt_end_date = case @unite
WHEN 'minute' THEN CONVERT(VARCHAR(20), @end_date_courante, 100)
WHEN 'hour' THEN CONVERT(VARCHAR(20), @end_date_courante, 100)
WHEN 'day' THEN REPLACE(CONVERT(VARCHAR(11), @end_date_courante, 106), ' ', '-')
WHEN 'month' THEN REPLACE(RIGHT(CONVERT(VARCHAR(11), @end_date_courante, 106), 8), ' ', '-')
WHEN 'year' THEN CONVERT(VARCHAR(20), datepart(year,@end_date_courante))
ELSE CONVERT(VARCHAR(20), @end_date_courante, 100) END
INSERT INTO @res (
StartDate,
EndDate,
TxtStartDate,
TxtEndDate,
IdDate) values(
@current_date,
@end_date_courante,
@txt_start_date,
@txt_end_date,
dbo.GetIDDate(@current_date,@unite,@interval)
)
set @current_date = @end_date_courante
end
return
end
Entonces, si quiero contar todos los usuarios agregados para cada intervalo de 33 minutos:
SELECT count(id_user) , timeTable.StartDate
FROM user
INNER JOIn dbo.[GetTableDate]('1970-01-01',datedate(),33,'minute') as timeTable
ON dbo.getIDDate(user.creation_date,'minute',33) = timeTable.IDDate
GRUPO POR dbo.getIDDate (user.creation_date, 'minute', 33) ORDER BY timeTable.StartDate
:)