MySQL группирует по интервалам в диапазоне дат
Вопрос
Я собираюсь построить график данных netflow, хранящихся в базе данных MySQL, и мне нужен эффективный способ получить соответствующие точки данных.Эти записи хранятся с датой в виде int в течение нескольких секунд, начиная с эпохи.Я хотел бы иметь возможность что-то вроде:
Select SUM(bytes) from table where stime > x and stime < Y
group by (10 second intervals)
Есть ли вообще способ это сделать?или было бы быстрее обработать это локально в python?даже для таблицы в 500 тысяч строк?
Редактировать
Моя ошибка в том, что время хранится как unsigned double вместо INT.В настоящее время я использую GROUP BY (FLOOR(stime / I))
где I - желаемый интервал.
Решение 5
Я использовал предложения из обоих ответов и от коллеги.Конечный результат заключается в следующем:
Select FROM_UNIXTIME(stime), bytes
from argusTable_2009_10_22
where stime > (UNIX_TIMESTAMP()-600)
group by floor(stime /10)
Я также попробовал решение с округлением, но результаты оказались противоречивыми.
Шанс
Другие советы
Возможно, вы сможете сделать это с помощью целочисленного деления.Не уверен в производительности.
Пусть I будет вашим желаемым интервалом в секундах.
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
Вы пробовали следующее?Просто разделите столбец tyiem на 10 и округлите результат в меньшую сторону.
SELECT SUM(bytes)
FROM table
WHERE stime > x
AND stime < Y
GROUP BY ROUND(stime/10, -1)
Я не знаю, работает ли функция ROUND() и группировка с вызовами функций в MySQL, хотя приведенное выше относится к T-SQL.
FLOOR
в группе by иногда терпит неудачу.иногда он группирует разные времена как одно значение, например, когда вы делите значение на 3, но он не делает то же самое, когда вы делите на 4, хотя разница между этими двумя значениями намного больше, чем 3 или 4, которые он должен группировать как две разные группы.Лучше приведите его к unsigned after floor, который работает следующим образом:
CAST(FLOOR(UNIX_TIMESTAMP(time_field)/I) AS UNSIGNED INT)
В чем проблема:
Иногда GROUP BY FLOOR(UNIX_TIMESTAMP(time_field)/3)
дает меньше групп по сравнению с GROUP BY FLOOR(UNIX_TIMESTAMP(time_field)/4)
что математически не должно быть возможным.
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
Я сделал это несколько лет назад, поэтому создал некоторую функцию (с sql server, но я предполагаю, что это почти то же самое) :
Сначала я создал скалярную функцию, которая возвращает мне идентификатор даты в зависимости от интервала и части даты (минута, час, день, мотылек, год):
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
Затем я создал табличную функцию, которая возвращает мне все идентификаторы между диапазоном дат :
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
Итак, если я хочу подсчитать всех пользователей, добавленных за каждый интервал в 33 минуты :
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
ГРУППИРОВАТЬ По dbo.getIDDate(user.creation_date,'минута',33) УПОРЯДОЧИВАТЬ По расписанию.Дата начала
:)