Группировать по интервалам данных

У меня есть отдельная таблица, в которой за период времени сохраняется пропускная способность сети. Один столбец будет содержать дату (первичный ключ), а другой столбец будет записывать полосу пропускания. Данные записываются каждую минуту. У нас будут другие столбцы, записывающие другие данные в этот момент времени.

Если пользователь запрашивает данные с 15-минутными интервалами (в течение 24-часового периода с датой начала и окончания), возможно ли с помощью одного запроса получить нужные мне данные или мне нужно будет записать хранимую процедуру/курсор сделать это? Затем пользователи могут запрашивать данные за 5 минут и т.д.

Я, скорее всего, буду использовать Postgres, но есть ли другие варианты NOSQL, которые были бы лучше?

Любые идеи?

Ответ 1

WITH t AS (
   SELECT ts, (random()*100)::int AS bandwidth
   FROM   generate_series('2012-09-01', '2012-09-04', '1 minute'::interval) ts
   )

SELECT date_trunc('hour', ts) AS hour_stump
      ,(extract(minute FROM ts)::int / 15) AS min15_slot
      ,count(*) AS rows_in_timeslice               -- optional
      ,sum(bandwidth) AS sum_bandwidth
FROM   t
WHERE  ts >= '2012-09-02 00:00:00+02'::timestamptz -- user time range
AND    ts <  '2012-09-03 00:00:00+02'::timestamptz -- careful with borders 
GROUP  BY 1, 2
ORDER  BY 1, 2;

CTE t предоставляет данные, подобные вашей таблице: одна временная метка ts в минуту с номером bandwidth. (Вам не нужна эта часть, вместо этого вы работаете со своей таблицей.)

Вот очень похожее решение для очень похожего вопроса - с подробным объяснением того, как работает эта конкретная агрегация:

Вот аналогичное решение для аналогичного вопроса о текущих суммах - с подробным объяснением и ссылками для различных используемых функций:

Дополнительный вопрос в комментарии

WITH -- same as above ...

SELECT DISTINCT ON (1,2)
       date_trunc('hour', ts) AS hour_stump
      ,(extract(minute FROM ts)::int / 15) AS min15_slot
      ,bandwidth AS bandwith_sample_at_min15
FROM   t
WHERE  ts >= '2012-09-02 00:00:00+02'::timestamptz
AND    ts <  '2012-09-03 00:00:00+02'::timestamptz
ORDER  BY 1, 2, ts DESC;

Получает один неагрегированный образец за 15-минутный интервал - из последней доступной строки в окне. Это будет 15-я минута, если строка не будет отсутствовать. Ключевыми частями являются DISTINCT ON и ORDER BY.
Более подробная информация о используемой технике здесь:

Ответ 2

select
    date_trunc('hour', d) + 
    (((extract(minute from d)::integer / 5 * 5)::text) || ' minute')::interval
    as "from",
    date_trunc('hour', d) + 
    ((((extract(minute from d)::integer / 5 + 1) * 5)::text) || ' minute')::interval
    - '1 second'::interval
    as "to",
    sum(random() * 1000) as bandwidth
from 
    generate_series('2012-01-01', '2012-01-31', '1 minute'::interval) s(d)
group by 1, 2
order by 1, 2
;

Это в течение 5 минут. В течение 15 минут разделите на 15.