MySQL groupe par intervalles dans une plage de dates
Question
Je vais représenter graphiquement les données netflow stockées dans une base de données MySQL et j'ai besoin d'un moyen efficace d'obtenir les points de données pertinents. Les enregistrements sont stockés avec la date sous la forme d'un entier pour les secondes depuis l'époque. J'aimerais pouvoir faire quelque chose comme:
Select SUM(bytes) from table where stime > x and stime < Y
group by (10 second intervals)
Y a-t-il un moyen de le faire? ou serait-il plus rapide de le gérer localement en python? même pour une table de 500K lignes?
MODIFIER
Mon erreur, l'heure est stockée comme un double non signé au lieu d'un INT.
J'utilise actuellement GROUP BY (FLOOR (stime / I))
où I correspond à l'intervalle souhaité.
La solution 5
J'ai utilisé les suggestions des deux réponses et d'un collègue. Le résultat final est le suivant:
Select FROM_UNIXTIME(stime), bytes
from argusTable_2009_10_22
where stime > (UNIX_TIMESTAMP()-600)
group by floor(stime /10)
J'ai aussi essayé la solution d'arrondi, mais les résultats étaient incohérents.
Chance
Autres conseils
Vous pourrez peut-être faire cela en utilisant une division entière. Pas sûr de la performance.
Laissez-moi être votre intervalle souhaité en secondes.
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
Avez-vous essayé ce qui suit? Il suffit de diviser la colonne tyiem par 10 et d’arrondir le résultat.
SELECT SUM(bytes)
FROM table
WHERE stime > x
AND stime < Y
GROUP BY ROUND(stime/10, -1)
Je ne sais pas si la fonction ROUND () et le regroupement d'appels de fonction fonctionnent dans MySQL, bien que ce qui précède soit en T-SQL.
FLOOR
dans le groupe par échoue parfois. il regroupe parfois différents moments sous la forme d'une valeur, par exemple, lorsque vous divisez la valeur avec 3, mais ne fait pas la même chose avec 4, bien que la différence entre ces deux valeurs soit bien supérieure à 3 ou 4, qu'il convient de regrouper deux groupes différents. Mieux vaut le placer sur non signé après le sol, qui fonctionne comme:
CAST(FLOOR(UNIX_TIMESTAMP(time_field)/I) AS UNSIGNED INT)
Le problème:
Parfois GROUP BY FLOOR (UNIX_TIMESTAMP (time_field) / 3)
donne moins de groupes que GROUP BY FLOOR (UNIX_TIMESTAMP (time_field) / 4)
qui est mathématiquement à ne pas ' t pas possible.
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
Je l'ai fait il y a quelques temps, j'ai donc créé une fonction (avec SQL Server, mais je suppose que c'est à peu près la même chose):
J'ai d'abord créé une fonction scalaire qui me renvoie l'identifiant d'une date en fonction d'un intervalle et d'une partie de date (minute, heure, jour, mois, année):
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
Ensuite, j'ai créé une fonction de table qui me renvoie tous les identifiants d’une plage de dates:
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
Donc, si je veux compter tous les utilisateurs ajoutés pour chaque intervalle de 33 minutes:
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
GROUP BY dbo.getIDDate (user.creation_date, 'minute', 33) ORDER BY timeTable.StartDate
: