Групповое заполнение пробелов в Postgresql/Timescaledb

129
13

У меня есть измерения с разных устройств, скажем, Device_A и Device_B. Для каждого устройства я измеряю температуру и влажность. Время от времени отсутствуют некоторые или все измерения: +---------------------+-------------+-------------+-------+ | ts | device_type | measurement | value | +---------------------+-------------+-------------+-------+ | 2018-04-30 23:59:59 | Device_A | Temperature | 10.1 | | 2018-04-30 23:59:59 | Device_A | Humidity | 66 | | 2018-04-30 23:59:59 | Device_B | Temperature | 19.1 | | 2018-05-03 23:59:59 | Device_A | Temperature | 12.1 | | 2018-05-03 23:59:59 | Device_B | Humidity | 67 | | 2018-05-03 23:59:59 | Device_B | Temperature | 16.1 | | 2018-05-04 23:59:59 | Device_A | Temperature | 17 | | 2018-05-04 23:59:59 | Device_A | Humidity | 63 | | 2018-05-04 23:59:59 | Device_B | Temperature | 12.1 | | 2018-05-04 23:59:59 | Device_B | Humidity | 73 | +---------------------+-------------+-------------+-------+ +---------------------+-------------+-------------+-------+ | ts | device_type | measurement | value | +---------------------+-------------+-------------+-------+ | 2018-04-30 23:59:59 | Device_A | Temperature | 10.1 | | 2018-04-30 23:59:59 | Device_A | Humidity | 66 | | 2018-04-30 23:59:59 | Device_B | Temperature | 19.1 | | 2018-05-03 23:59:59 | Device_A | Temperature | 12.1 | | 2018-05-03 23:59:59 | Device_B | Humidity | 67 | | 2018-05-03 23:59:59 | Device_B | Temperature | 16.1 | | 2018-05-04 23:59:59 | Device_A | Temperature | 17 | | 2018-05-04 23:59:59 | Device_A | Humidity | 63 | | 2018-05-04 23:59:59 | Device_B | Temperature | 12.1 | | 2018-05-04 23:59:59 | Device_B | Humidity | 73 | +---------------------+-------------+-------------+-------+

Я хочу получать среднюю температуру и влажность в течение каждого дня, когда нет данных, я хочу, чтобы это было 0 (или любое другое произвольное значение) - интересные моменты на 2018-05-01 и 2018-05-02 +---------------------+-------------+-------+ | date | measurement | mean | +---------------------+-------------+-------+ | 2018-04-30 23:59:59 | Humidity | 66 | | 2018-04-30 23:59:59 | Temperature | 14.6 | | 2018-05-01 23:59:59 | Temperature | 0 | | 2018-05-01 23:59:59 | Humidity | 0 | | 2018-05-02 23:59:59 | Temperature | 0 | | 2018-05-02 23:59:59 | Humidity | 0 | | 2018-05-03 23:59:59 | Humidity | 67 | | 2018-05-03 23:59:59 | Temperature | 14.1 | | 2018-05-04 23:59:59 | Humidity | 68 | | 2018-05-04 23:59:59 | Temperature | 14.55 | +---------------------+-------------+-------+ +---------------------+-------------+-------+ | date | measurement | mean | +---------------------+-------------+-------+ | 2018-04-30 23:59:59 | Humidity | 66 | | 2018-04-30 23:59:59 | Temperature | 14.6 | | 2018-05-01 23:59:59 | Temperature | 0 | | 2018-05-01 23:59:59 | Humidity | 0 | | 2018-05-02 23:59:59 | Temperature | 0 | | 2018-05-02 23:59:59 | Humidity | 0 | | 2018-05-03 23:59:59 | Humidity | 67 | | 2018-05-03 23:59:59 | Temperature | 14.1 | | 2018-05-04 23:59:59 | Humidity | 68 | | 2018-05-04 23:59:59 | Temperature | 14.55 | +---------------------+-------------+-------+

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

Есть ли способ генерировать вывод, подобный приведенному выше? Я знаю, что перенос данных из "длинного" в "широкий" формат мог бы решить мою проблему, но интересно, есть ли другое решение?

Мой код:

CREATE SCHEMA tmp ;
SET search_path = tmp;

DROP TABLE IF EXISTS sample_data CASCADE;
CREATE TABLE sample_data (
"ts" TIMESTAMP WITHOUT TIME ZONE NOT NULL,
"device_type" character varying,
"measurement" character varying,
"value" DOUBLE PRECISION
);

INSERT INTO sample_data(ts, device_type, measurement, value) VALUES
('2018-04-30 23:59:59', 'Device_A', 'Temperature', 10.1),
('2018-04-30 23:59:59', 'Device_A', 'Humidity', 66.0),
('2018-04-30 23:59:59', 'Device_B', 'Temperature', 19.1),
('2018-05-03 23:59:59', 'Device_A', 'Temperature', 12.1),
('2018-05-03 23:59:59', 'Device_B', 'Humidity', 67.0),
('2018-05-03 23:59:59', 'Device_B', 'Temperature', 16.1),
('2018-05-04 23:59:59', 'Device_A', 'Temperature', 17.0),
('2018-05-04 23:59:59', 'Device_A', 'Humidity', 63.0),
('2018-05-04 23:59:59', 'Device_B', 'Temperature', 12.1),
('2018-05-04 23:59:59', 'Device_B', 'Humidity', 73.0)
;

WITH period AS (
SELECT date
FROM generate_series('2018-04-30 23:59:59'::timestamp,
'2018-05-04 23:59:59', interval '1 day') date
),
sample AS ( SELECT * FROM sample_data)

SELECT period.date,
measurement,
coalesce(sum(sample.value), 0) AS value
FROM period
LEFT JOIN sample ON period.date = sample.ts
GROUP BY
period.date,
sample.measurement
ORDER BY period.date,
sample.measurement
;

Выход: +---------------------+-------------+-------+ | date | measurement | mean | +---------------------+-------------+-------+ | 2018-04-30 23:59:59 | Humidity | 66 | | 2018-04-30 23:59:59 | Temperature | 14.6 | | 2018-05-01 23:59:59 | NULL | 0 | | 2018-05-02 23:59:59 | NULL | 0 | | 2018-05-03 23:59:59 | Humidity | 67 | | 2018-05-03 23:59:59 | Temperature | 14.1 | | 2018-05-04 23:59:59 | Humidity | 68 | | 2018-05-04 23:59:59 | Temperature | 14.55 | +---------------------+-------------+-------+ +---------------------+-------------+-------+ | date | measurement | mean | +---------------------+-------------+-------+ | 2018-04-30 23:59:59 | Humidity | 66 | | 2018-04-30 23:59:59 | Temperature | 14.6 | | 2018-05-01 23:59:59 | NULL | 0 | | 2018-05-02 23:59:59 | NULL | 0 | | 2018-05-03 23:59:59 | Humidity | 67 | | 2018-05-03 23:59:59 | Temperature | 14.1 | | 2018-05-04 23:59:59 | Humidity | 68 | | 2018-05-04 23:59:59 | Temperature | 14.55 | +---------------------+-------------+-------+

спросил(а) 2021-01-19T20:28:40+03:00 9 месяцев назад
1
Решение
137

Просто нашел ответ - таблица периодов также должна содержать измерения:

WITH period AS (
SELECT date, m.measurement
FROM generate_series('2018-04-30 23:59:59'::timestamp, '2018-05-04 23:59:59', interval '1 day') date
NATURAL JOIN
(SELECT DISTINCT measurement FROM sample_data) m
)

SELECT period.date,
period.measurement,
coalesce(sum(sample_data.value), 0) AS value
FROM period
LEFT JOIN sample_data ON period.date = sample_data.ts AND period.measurement = sample_data.measurement
GROUP BY
period.date,
period.measurement
ORDER BY
period.date,
period.measurement
;

ответил(а) 2021-01-19T20:28:40+03:00 9 месяцев назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема