БД дизайн
Несмотря на то, что вы можете работать с отдельными столбцами date
и time
, на самом деле нет никакого преимущества перед одним столбцом timestamp
. Я бы адаптировался:
ALTER TABLE tbl ADD column ts timestamp;
UPDATE tbl SET ts = date + time; -- assuming actual date and time types
ALTER TABLE tbl DROP column date, DROP column time;
Если дата и время не являются действительными типами данных date
и time
, используйте to_timestamp()
. Связанный:
Запрос
Тогда запрос немного проще:
SELECT *
FROM (
SELECT sn, generate_series(min(ts), max(ts), interval '5 min') AS ts
FROM tbl
WHERE sn = '4as11111111'
AND ts >= '2018-01-01'
AND ts < '2018-01-02'
GROUP BY 1
) grid
CROSS JOIN LATERAL (
SELECT round(avg(vin1), 2) AS vin1_av
, round(avg(vin2), 2) AS vin2_av
, round(avg(vin3), 2) AS vin3_av
FROM tbl
WHERE sn = grid.sn
AND ts >= grid.ts
AND ts < grid.ts + interval '5 min'
) avg;
дБ <> скрипка здесь
Генерирует сетку времени начала в первом подзапросе grid
, начиная с первой до последней квалифицирующей строки в заданном временном интервале.
Присоединение к строкам, попадающим в каждый раздел, с помощью LATERAL
объединения и немедленное агрегирование средних значений в подзапросе avg
. Из-за агрегатов он всегда возвращает строку, даже если записи не найдены. Среднее значение по умолчанию NULL
в этом случае.
Результат включает в себя все временные интервалы между первой и последней квалификационной строкой в данном временном интервале. Различные другие составы результата также имели бы смысл. Например, включая все временные интервалы в заданном временном интервале или только временные интервалы с фактическими значениями. Все возможное, мне пришлось выбрать одну интерпретацию.
Индекс
По крайней мере, иметь этот индекс из нескольких столбцов:
CRATE INDEX foo_idx ON tbl (sn, ts);
Или на (sn, ts, vin1, vin2, vin3)
, чтобы разрешить сканирование только по индексу - если выполнены некоторые предварительные условия, особенно если строки таблицы намного шире, чем в демонстрационной версии.
Близко связаны:
На основании вашей исходной таблицы
В соответствии с просьбой и разъяснением в комментарии , а затем снова обновляется в вопросе, чтобы включить столбцы mac
и loc
. Я предполагаю, что вы хотите отдельные средние значения для (mac, loc)
.
date
и time
по-прежнему являются отдельными столбцами, столбцы vin * имеют тип float
и исключают временные интервалы без строк:
Обновленный запрос также перемещает функцию возврата набора generate_series()
в список FROM
, который является более чистым, чем Postgres 10:
SELECT t.mac, sn.sn, t.loc, ts.ts::time AS time, ts.ts::date AS date
, t.vin1_av, t.vin2_av, t.vin3_av
FROM (SELECT text '4as11111111') sn(sn) -- provide sn here once
CROSS JOIN LATERAL (
SELECT min(date+time) AS min_ts, max(date+time) AS max_ts
FROM tbl
WHERE sn = sn.sn
AND date+time >= '2018-01-01 0:0' -- provide time frame here
AND date+time < '2018-01-02 0:0'
) grid
CROSS JOIN LATERAL generate_series(min_ts, max_ts, interval '5 min') ts(ts)
CROSS JOIN LATERAL (
SELECT mac, loc
, round(avg(vin1)::numeric, 2) AS vin1_av -- cast to numeric for round()
, round(avg(vin2)::numeric, 2) AS vin2_av -- but rounding is optional
, round(avg(vin3)::numeric, 2) AS vin3_av
FROM tbl
WHERE sn = sn.sn
AND date+time >= ts.ts
AND date+time < ts.ts + interval '5 min'
GROUP BY mac, loc
HAVING count(*) > 0 -- exclude empty slots
) t;
Создайте индекс выражения из нескольких столбцов для поддержки этого:
CRATE INDEX bar_idx ON tbl (sn, (date+time));
дБ <> скрипка здесь
Но я бы предпочел все время использовать timestamp
.