СУБД временных рядов для нерегулярных данных с пробелами - PullRequest
0 голосов
/ 09 апреля 2020

Здравствуйте, уважаемое сообщество Stackoverflow,

существует ли механизм базы данных, который может заполнять пробелы в нерегулярных временных рядах, а также группировать их?

Самая эффективная система - InfluxDB:

> select * from test;
name: test
time value1 value2
---- ------ ------
1    1      0
2    2      
3    3      
4    4      
5    5      2
6    6      
7    7      
8    8      

> select * from test fill(previous);
name: test
time value1 value2
---- ------ ------
1    1      0
2    2      0
3    3      0
4    4      0
5    5      2
6    6      2
7    7      2
8    8      2

К сожалению, в нем нет группировки по ключам, которые не являются тегами. Мне нужно сгруппировать по любому столбцу, хотя:

> select mean(value1) from (select value1, value2 from test fill(previous)) group by value2;
name: test
tags: value2=
time mean
---- ----
0    4.5

Я также пробовал TimescaleDB + Postre SQL, но TimescaleDB имеет плохую функцию заполнения пробелов. Я использую миллисекунды с начала эпохи как метку времени в моих реальных данных, и TimescaleDB заполняет все промежутки между моими строками, даже если я ничего не регистрировал за определенный промежуток времени. Из-за выборки TimescaleDB таблица с 17-тысячными строками стала таблицей с 500-тысячными строками.

Редактировать:

Поскольку меня попросили дать больше информации о моей попытке полного заполнения мои потребности с TimescaleDB; Я следил за информацией на этой странице:

https://blog.timescale.com/blog/sql-functions-for-time-series-analysis/

locf делает то, что я хочу.

Проблема в том, что locf нельзя использовать без функции агрегирования, такой как avg . Вы должны пройти очень разные инверсии времени, как описано здесь:

https://docs.timescale.com/latest/api#locf

Я анализирую состояния клапанов. Они могут быть как открытыми, так и закрытыми. Ради времени загрузки я загружаю данные только тогда, когда клапан действительно переключает свое состояние. Но я также хочу знать, открыт ли клапан, когда другой клапан открыт. Поэтому мне нужно заполнить все пробелы в каждом ряду или, другими словами, выполнить «последнее наблюдение перенесено».

В одном из моих наборов тестовых данных содержится 17000 рядов переключателей клапанов. Если я установлю 1 мс в качестве временного интервала (потому что в каждую мс может клапан начать переключать свое состояние) в ordner для использования locf с avg за одно наблюдение, я не буду получить, например, 0,5 для состояния клапана. Он может быть открыт или закрыт. Но мое число строк увеличилось с 17000 до 500000 из-за функции time_bucket_gapfill () .

Запрос был как-то похож на это:

SELECT time_bucket_gapfill
(
'1 millisecond',
ts::TIMESTAMP,
start => '2020-03-11 11:09:49',
finish => '2020-03-11 11:17:16'
) AS tsi,
LOCF(avg(airpressure1))
FROM (SELECT TO_TIMESTAMP(TIME::DOUBLE precision / 1000.0)::TIMESTAMP AS TS,
             airpressure1,
             airpressure2,
             airpressure3,
             airpressure4
      FROM ts_data
      WHERE process_id = 1
      ORDER BY TO_TIMESTAMP(TIME::DOUBLE precision / 1000.0)::TIMESTAMP DESC) AS T
GROUP BY tsi;

Edit 2

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

SELECT TIME,
       airpressure1 as air1,
       safetyvlv_out1 as safety1,
       airpressure2 as air2,
       safetyvlv_out2 as safety2,
       airpressure3 as air3,
       safetyvlv_out3 as safety3,
       airpressure4 as air4,
       safetyvlv_out4 as safety4
FROM ts_data_gaps
WHERE airpressure1 IS NOT NULL
OR    safetyvlv_out1 IS NOT NULL;

time    air1    safety1 air2    safety2 air3    safety3 air4    safety4
1583921837975       1.0     1.0     1.0     1.0
1583921844020       0.0     0.0     0.0     0.0
1583921868224       1.0                     
1583921878845   1.0                         
1583921878985   0.0                         
1583921879798   1.0                         
1583921879900   0.0                         
1583921880537       0.0                     
1583921915212       1.0     1.0     1.0     1.0
1583921919219   1.0     1.0     1.0     1.0 
1583921919488   0.0     0.0     0.0     0.0 
1583921923488   1.0     1.0     1.0     1.0 
1583921926491   0.0     0.0     0.0     0.0 
1583921930497   1.0     1.0     1.0     1.0 
1583921933501   0.0     0.0     0.0     0.0 
1583921934008       0.0     0.0     0.0     0.0
1583921389639       1.0     1.0     1.0     1.0
1583921395681       0.0     0.0     0.0     0.0
1583921415256       1.0                     
1583921425912   1.0                         
1583921426027   0.0                         
1583921426729   1.0                         
1583921426837   0.0                         
1583921427043   1.0                         
1583921427084   0.0                         
1583921427582       0.0     

Когда я делаю что-то подобное в 17k строках:

create view z as
SELECT TIME,
       airpressure1 as air1,
       safetyvlv_out1 as safety1,
       airpressure2 as air2,
       safetyvlv_out2 as safety2,
       airpressure3 as air3,
       safetyvlv_out3 as safety3,
       airpressure4 as air4,
       safetyvlv_out4 as safety4
FROM ts_data_gaps
WHERE airpressure1 IS NOT NULL
OR    safetyvlv_out1 IS NOT NULL;

SELECT time_bucket_gapfill
(
'1 millisecond',
ts::TIMESTAMP,
start => '2020-03-11 03:41:08',
finish => '2020-03-11 11:18:54'
) AS tsi,
LOCF(avg(air1)) as air1,
LOCF(avg(safety1)) as safety1
FROM (SELECT TO_TIMESTAMP(TIME::DOUBLE precision / 1000.0)::TIMESTAMP AS TS,
             air1, safety1
      FROM z
      ORDER BY TO_TIMESTAMP(TIME::DOUBLE precision / 1000.0)::TIMESTAMP DESC) AS T
GROUP BY tsi;

.. Черт возьми, P C с 32 ГБ ОЗУ не хватает памяти!

Спасибо!

1 Ответ

1 голос
/ 09 апреля 2020

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

select * from test fill(previous);
name: test
time value1 value2
---- ------ ------
1    1      0
2    2      0
3    3      0
4    4      0
5    5      2
6    6      2
7    7      2
8    8      2

в PostgreSQL (и TimescaleDB) с помощью

create view x as select time, value2 from test where value2 is not null;
select * from x;
time value2
---- ------
1    0
5    2
create view y as 
   select time t_start, lead(time, 1) over (order by time) t_end, value2 from x;
 select * from y;
t_start, t_end, value2
-------  -----  ------
1        5      0
5               2
select test.time, test.value1, y.value2
from test 
join y 
  on test.time >=y.t_start and (test.time<y.t_end or t_end is null);

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

...