SQL для группировки / сжатия по времени для отчета динамически - PullRequest
2 голосов
/ 01 сентября 2011

Как можно динамически сжимать / агрегировать / группировать таблицу с событиями.У меня есть таблица со значениями и временем появления.

Примерно так:

value_col   time_col
3         | 2011-02-16 22:21:05.250
2         | 2011-02-16 21:21:06.170
15        | 2011-02-16 21:21:05.250

Мне нужно агрегировать значения по заданному промежутку времени (например, по часам), начиная с первой строки(последнее мероприятие).Итак, в этом примере я хочу получить две строки для ежечасной агрегации.

5
15

Так что, если приходит новое значение:

value_col   time_col
6         | 2011-02-16 23:21:05.247
3         | 2011-02-16 22:21:05.250
2         | 2011-02-16 21:21:06.170
15        | 2011-02-16 21:21:05.250

Если я выполню этот запрос еще раз, яхочу в итоге:

9
17

Должно быть легко изменить временной интервал в запросе.Например, сжатие за последние 30 секунд, последние 6 часов, последние 24 часа и т. Д. Как я могу это сделать в Oracle и MS SQL?

Ответы [ 5 ]

1 голос
/ 04 сентября 2011

Благодаря предыдущим ответам я получил представление о том, как выполнить все требования.

Для каждой записи я вычисляю разницу во времени с последней записью в миллисекундах (или секундах, в зависимости от разрешения). Затем я делаю разницу по модулю на промежуток времени, который меня интересует (например, 3600 с = 1 час). Затем я добавляю это значение к time_col той же записи и группирую по нему.

Создать таблицу:

CREATE TABLE [dbo].[test_table](
    [value_col] [int] NOT NULL,
    [time_col] [datetime] NOT NULL
) ON [PRIMARY]
GO
INSERT [dbo].[test_table] ([value_col], [time_col]) VALUES (3, CAST(0x00009E8C01705737 AS DateTime))
INSERT [dbo].[test_table] ([value_col], [time_col]) VALUES (2, CAST(0x00009E8C015FDD8B AS DateTime))
INSERT [dbo].[test_table] ([value_col], [time_col]) VALUES (15, CAST(0x00009E8C015FDC77 AS DateTime))
INSERT [dbo].[test_table] ([value_col], [time_col]) VALUES (6, CAST(0x00009E8C0180D1F6 AS DateTime))

Решение для SQL:

SELECT SUM(value_col) AS s_val, aggregation_time FROM 
 (SELECT value_col, time_col, 
  DATEADD(millisecond,DATEDIFF(millisecond,time_col,(SELECT MAX(time_col) 
  FROM test_table)) % (3600 * 1000), time_col) AS aggregation_time 
 FROM test_table)
GROUP BY aggregation_time
ORDER BY aggregation_time DESC

Решение для Oracle:

SELECT SUM(value_col) as s_val, aggregation_time FROM
 (SELECT value_col, time_col + 
  (MOD(ROUND(((CAST((SELECT MAX(time_col) FROM test_table) AS DATE ) - 
  CAST(time_col AS DATE ))*86400),0),3600))/86400 as aggregation_time
  FROM test_table l)     
GROUP BY aggregation_time
ORDER BY aggregation_time DESC

Если я хочу агрегировать за последние 2 часа, я просто изменяю 3600 на 7200 секунд.

Результат:

9   2011-02-16 23:21:05.247
17  2011-02-16 22:21:05.247
0 голосов
/ 01 сентября 2011

Вот вариант Oracle, использующий только один доступ к таблице.

SQL> create table t (value,mydate)
  2  as
  3  select 3, to_timestamp('2011-02-16 22:21:05.250','yyyy-mm-dd hh24:mi:ss.ff3') from dual union all
  4  select 2, to_timestamp('2011-02-16 21:21:05.267','yyyy-mm-dd hh24:mi:ss.ff3') from dual union all
  5  select 15, to_timestamp('2011-02-16 21:21:05.155','yyyy-mm-dd hh24:mi:ss.ff3') from dual
  6  /

Table created.

Следующие запросы группируются по разнице в часах, считая от самой последней отметки времени, которая, по-видимому, соответствует желаемому:

SQL> select sum(value)
  2    from ( select extract(hour from (max(mydate) over () - mydate)) difference_in_hours
  3                , value
  4             from t
  5         )
  6   group by difference_in_hours
  7   order by difference_in_hours
  8  /

SUM(VALUE)
----------
         5
        15

2 rows selected.

Но, по-видимому, ваш пример не точен, потому что, когда я добавляю четвертую строку из вашего примера, значение 15 находится более чем в двух часах от самой последней отметки времени, что приводит к дополнительной группе:

SQL> insert into t values (6,to_timestamp('2011-02-16 23:21:05.249','yyyy-mm-dd hh24:mi:ss.ff3'))
  2  /

1 row created.

SQL> select sum(value)
  2    from ( select extract(hour from (max(mydate) over () - mydate)) difference_in_hours
  3                , value
  4             from t
  5         )
  6   group by difference_in_hours
  7   order by difference_in_hours
  8  /

SUM(VALUE)
----------
         9
         2
        15

3 rows selected.

Так я неправильно истолковал ваше требование или у вас есть ошибка в вашем примере?

С уважением,
Роб.

0 голосов
/ 01 сентября 2011

Вот пример, как агрегировать ежечасно:

SELECT TO_CHAR(TRUNC(a.created, 'HH24'), 'DD.MM.YYYY HH24:MI'), COUNT(*)
FROM all_objects a
GROUP BY TRUNC(a.created, 'HH24');

Это дает вам количество объектов из all_objects, агрегированных по часам по времени их создания. Ключ TRUNC(column, 'HH24'), который агрегирует ваши данные ежечасно.

В вашем случае что-то вроде этого:

create table t (i int, d date);
insert into t values (3, to_date('2011-02-16 22:21:05', 'YYYY-MM-DD HH24:MI:SS'));
insert into t values (2, to_date('2011-02-16 21:21:05', 'YYYY-MM-DD HH24:MI:SS'));
insert into t values (15, to_date('2011-02-16 21:21:05', 'YYYY-MM-DD HH24:MI:SS'));
commit;
select sum(i), TO_CHAR(TRUNC(t.d, 'HH24'), 'DD.MM.YYYY HH24:MI') from t group by TRUNC(t.d, 'HH24');
0 голосов
/ 01 сентября 2011

Для SQLServer у вас будет что-то вроде

SELECT DATEDIFF(hour,b.date_time_col,a.dt), SUM(b.id)
FROM (SELECT MAX(date_time_col) as dt FROM table1)a,  
table1 b
GROUP BY DATEDIFF(hour,b.date_time_col,a.dt)

У Oracle нет DATE_DIFF, эквивалент будет TRUNC(24*(a.dt-b.date_time_col))

0 голосов
/ 01 сентября 2011
 a              b
3  | 2011-02-16 23:21:05.250
2  | 2011-02-16 22:21:05.267
15 | 2011-02-16 22:21:05.155

with tmp as (
  select a, to_char(b, 'YYYYMMDDHH24') h from tab
)
select sum(a), h from tmp group by h
/
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...