2016-11-16 5 views
2

Вдохновленный этим great answer Я написал следующий запрос, который возвращает AVG, рассчитанный в соответствии с 5-минутными интервалами за последний год.как вставить негруппированные данные

То, что я хотел бы иметь это все 5-минутными интервалами и, в случае, установите значение null, если ни одна строка не помещается в частности промежутка времени.

with intervals as (select 
        (select min("timestamp") from public.hst_energy_d) + n AS start_timestamp, 
        (select min("timestamp") from public.hst_energy_d) + n + 299 AS end_timestamp 
        from generate_series(extract(epoch from now())::BIGINT - 10596096000, extract(epoch from now())::BIGINT, 300) n) 
(SELECT AVG(meas."Al1") as "avg", islots.start_timestamp AS "timestamp" 
FROM public.hst_energy_d meas 
    RIGHT OUTER JOIN intervals islots 
    on meas.timestamp >= islots.start_timestamp and meas.timestamp <= islots.end_timestamp 
WHERE 
    meas.idinstrum = 4 
    AND 
    meas.id_device = 122 
    AND 
    meas.timestamp > extract(epoch from now()) - 10596096000 
GROUP BY islots.start_timestamp, islots.end_timestamp 
ORDER BY timestamp); 

ответ

1

Я думаю, что я вижу, что вы пытаетесь сделать, и мне интересно, если с помощью interval '5 minutes' Обильно would't быть лучше и легче следовать подходу:

with times as ( -- find the first date in the dataset, up to today 
    select 
    date_trunc ('minutes', min("timestamp")) - 
    mod (extract ('minutes' from min("timestamp"))::int, 5) * interval '1 minute' as bt, 
    date_trunc ('minutes', current_timestamp) - 
    mod (extract ('minutes' from current_timestamp)::int, 5) * interval '1 minute' as et 
    from hst_energy_d 
    where 
    idinstrum = 4 and 
    id_device = 122 
), -- generate every possible range between these dates 
ranges as (
    select 
    generate_series(bt, et, interval '5 minutes') as range_start 
    from times 
), -- normalize your data to which 5-minut interval it belongs to 
rounded_hst as (
    select 
    date_trunc ('minutes', "timestamp") - 
    mod (extract ('minutes' from "timestamp")::int, 5) * interval '1 minute' as round_time, 
    * 
    from hst_energy_d 
    where 
    idinstrum = 4 and 
    id_device = 122 
) 
select 
    r.range_start, r.range_start + interval '5 minutes' as range_end, 
    avg (hd."Al1") 
from 
    ranges r 
    left join rounded_hst hd on 
    r.range_start = hd.round_time 
group by 
    r.range_start 
order by 
    r.range_start 

Кстати, проницательный глаз может интересно, зачем беспокоиться о CTE rounded_hst и почему бы просто не использовать «между» в соединении. Из всего, что я проверил и наблюдал, база данных взорвет все возможности, а затем проверит условие между условиями в том, что составляет предложение where - фильтрованный декартес. Для этого много интервалов, это гарантированно будет убийцей.

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

- EDIT 11/17/2016 -

Решение от OP, который принимает во внимание времена номера, а не даты:

with times as ( -- find the first date in the dataset, up to today 
    select 
     date_trunc('minutes', to_timestamp(min("timestamp"))::timestamp) - 
     mod(extract ('minutes' from to_timestamp(min("timestamp"))::timestamp)::int, 5) * interval '1 minute' as bt, 
     date_trunc('minutes', current_timestamp::timestamp) - 
     mod(extract ('minutes' from (current_timestamp)::timestamp)::int, 5) * interval '1 minute' as et 
    from hst_energy_d 
    where 
     idinstrum = 4 and 
     id_device = 122 
), -- generate every possible range between these dates 
    ranges as (
     select 
     generate_series(bt, et, interval '5 minutes') as range_start 
     from times 
), -- normalize your data to which 5-minute interval it belongs to 
    rounded_hst as (
     select 
     date_trunc ('minutes', to_timestamp("timestamp")::timestamp)::timestamp - 
     mod (extract ('minutes' from (to_timestamp("timestamp")::timestamp))::int, 5) * interval '1 minute' as round_time, 
     * 
     from hst_energy_d 
     where 
     idinstrum = 4 and 
     id_device = 122 
) 
select 
    extract('epoch' from r.range_start)::bigint, extract('epoch' from r.range_start + interval '5 minutes')::bigint as range_end, 
    avg (hd."Al1") 
from 
    ranges r 
    left join rounded_hst hd on 
          r.range_start = hd.round_time 
group by 
    r.range_start 
order by 
    r.range_start; 
+0

, что аккуратный запрос! К сожалению, 'timestamp' является' BIGINT' вместо реальной «timestamp», поэтому моя математика с числами вместо timestamps – Bertuz

+0

Я адаптировал ваш предложенный код для работы с bigint, но я не уверен, что это все еще возможно используйте более легкий SQL. [gist to the new SQL] (https://gist.github.com/bertuz/5544663474b8a0850dcede03d1903a02) можете ли вы взглянуть и дать мне несколько отзывов? Плюс: вы говорили о том, что Postgres лучше справляются с вашим решением: как вы анализировали это? Используя 'EXPLAIN'? Вот [запрос плана] (https://gist.github.com/bertuz/5f06ab81cde3e78231c94c3a76ad20f8), который выполняет мой запрос. Действительно ли он выполняет два сканирования по hst_energy_d? Это много*! Спасибо – Bertuz

+0

Хорошо, 'EXPLAIN' говорит сам за себя: ваши решения стоят' 48.47', в мистерах стоит 247.17' – Bertuz

Смежные вопросы