Postgres SQL выбирает диапазон записей, разнесенных на заданный интервал - PullRequest
4 голосов
/ 15 марта 2011

Я пытаюсь определить, возможно ли, используя только sql для postgres, выбрать диапазон упорядоченных во времени записей с заданным интервалом.

Допустим, у меня есть 60 записей, по одной записи на каждую минуту в данный час.Я хочу выбирать записи с 5-минутными интервалами для этого часа.Результирующие строки должны содержать 12 записей с интервалом в 5 минут.

В настоящее время это достигается путем выбора полного диапазона записей, а затем повторения результатов и извлечения записей через заданный интервал.Я пытаюсь понять, могу ли я сделать это аккуратно в sql, поскольку наша база данных велика, и мы можем иметь дело с десятками тысяч записей.

Есть мысли?

Ответы [ 5 ]

6 голосов
/ 15 марта 2011

Да, вы можете.Это действительно легко, когда вы освоитесь.Я думаю, что это одна из жемчужин SQL и особенно легко в PostgreSQL из-за его превосходной временной поддержки.Часто сложные функции могут превращаться в очень простые запросы в SQL, которые можно масштабировать и правильно индексировать.

При этом используется generate_series для составления выборки временных отметок, которые разнесены на 1 минуту.Затем внешний запрос извлекает минуты и использует модуль по модулю, чтобы найти значения с интервалом в 5 минут.

select
    ts,
    extract(minute from ts)::integer as minute

    from
    ( -- generate some time stamps - one minute apart
        select
            current_time + (n || ' minute')::interval  as ts
        from generate_series(1, 30) as n
    ) as timestamps
    -- extract the minute check if its on a 5 minute interval
    where extract(minute from ts)::integer % 5 = 0
    -- only pick this hour 
    and extract(hour from ts) = extract(hour from current_time)
;
         ts         | minute 
--------------------+--------
 19:40:53.508836-07 |     40
 19:45:53.508836-07 |     45
 19:50:53.508836-07 |     50
 19:55:53.508836-07 |     55

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

Я однажды написал систему резервирования в PostgreSQL (в которой было много временной логики, где интервалы дат не могли перекрываться), и мне никогда не приходилось прибегать к итеративнымметоды.

http://www.amazon.com/SQL-Design-Patterns-Programming-Focus/dp/0977671542 - отличная книга, в которой есть много интервальных примеров.Трудно найти в книжных магазинах сейчас, но оно того стоит.

1 голос
/ 15 марта 2011
  • Если интервалы не основаны на времени, и вы просто хотите каждую 5-ю строку; или
  • Если время регулярное и у вас всегда есть одна запись в минуту

Ниже приводится одна запись на каждые 5

select *
from
(
  select *, row_number() over (order by timecolumn) as rown
  from tbl
) X
where mod(rown, 5) = 1

Если ваши записи времени не являются регулярными, то вам нужно сгенерировать временной ряд (приведенный в другом ответе) и оставить его в своей таблице, сгруппировать по столбцу времени (из ряда) и выбрать максимальное время из вашего таблица, которая меньше столбца времени.

Псевдо

select thetimeinterval, max(timecolumn)
from ( < the time series subquery > ) X
left join tbl on tbl.timecolumn <= thetimeinterval
group by thetimeinterval

И далее присоедините его обратно к таблице для полной записи (при условии уникального времени)

select t.* from
tbl inner join
(
    select thetimeinterval, max(timecolumn) timecolumn
    from ( < the time series subquery > ) X
    left join tbl on tbl.timecolumn <= thetimeinterval
    group by thetimeinterval
) y on tbl.timecolumn = y.timecolumn
1 голос
/ 15 марта 2011

Извлеките минуты, конвертируйте в int4 и посмотрите, равен ли остаток от деления на 5 0:

select * 
  from TABLE 
  where int4 (date_part ('minute', COLUMN)) % 5 = 0; 
0 голосов
/ 15 марта 2011

Это предполагает, что ваши пятиминутные интервалы, так сказать, «на пятерках». То есть, что вы хотите 07:00, 07:05, 07:10, а не 07:02, 07:07, 07:12. Также предполагается, что у вас нет двух строк в одну и ту же минуту, что может быть неверным предположением.

select your_timestamp
from your_table
where cast(extract(minute from your_timestamp) as integer) in (0,5);

Если у вас может быть две строки с метками времени в течение одной минуты, например

2011-01-01 07:00:02
2011-01-01 07:00:59

тогда эта версия безопаснее.

select min(your_timestamp)
from your_table
group by (cast(extract(minute from your_timestamp) as integer) / 5)

Оберните любой из них в представление, и вы можете присоединить его к вашему базовому столу.

0 голосов
/ 15 марта 2011

Как насчет этого:

select min(ts), extract(minute from ts)::integer / 5 
   as bucket group by bucket order by bucket; 

Это дает преимущество в том, что вы делаете правильные вещи, если у вас есть два показания за одну минуту, или ваши показания пропускают минуту.Вместо использования min еще лучше использовать одну из агрегатных функций first () - код, который вы можете найти здесь:

http://wiki.postgresql.org/wiki/First_%28aggregate%29

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...