2015-04-23 4 views
4

Я бегу Postgres 9.2 и у меня есть большой стол что-то вродескользящее среднее Postgres

CREATE TABLE sensor_values 
(
    ts timestamp with time zone NOT NULL, 
    value double precision NOT NULL DEFAULT 'NaN'::real, 
    sensor_id integer NOT NULL 
) 

У меня есть ценности, поступающие в систему постоянно, то есть многие в минуту. Я хочу поддерживать текущее стандартное отклонение/среднее значение для последних 200 значений, поэтому я могу определить, находятся ли новые значения, входящие в систему, с тремя стандартными отклонениями среднего значения. Для этого мне понадобится текущее стандартное отклонение, и оно будет постоянно обновляться для последних 200 значений. Поскольку таблица может быть сотнями миллионов строк, я не хочу, чтобы последние сказали 200 строк для датчика, заказанного по времени, а затем выполнили vg (value), var_samp (значение) для каждого нового значения. I и предполагая он будет быстрее обновлять стандартное отклонение и среднее значение.

Я начал писать функцию PL/pgSQL, чтобы обновить отклонение качения и значение для каждого нового значения, вводимого системой для конкретного датчика.

Я могу сделать это с помощью псевдо-код, как

newavg = oldavg + (new_value - old_value)/window_size 
new_variance += (new_value-old_value)*(new_value-newavg+old_value-oldavg)/(window_size-1) 

Это основано на http://jonisalonen.com/2014/efficient-and-accurate-rolling-standard-deviation/

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

The first value of the window. 
The mean average of the window values. 
The variance of the window values. 

Таким образом, я не должен постоянно получать там последние 200 значения и сделать сумму etc.I можно повторно использовать эти значения, когда новое значение датчика Заходите.

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

что-то вроде

WITH s AS 
     (SELECT value FROM sensor_values WHERE sensor_values.sensor_id = $1 AND ts >= (NOW() - INTERVAL '2 day')::timestamptz ORDER BY ts DESC LIMIT 200) 
    SELECT avg(value), var_samp(value) INTO last_window_average, last_window_variance FROM s; 

Но как я могу получить последнее значение (ealiest), чтобы спасти от этого оператора выбора? Могу ли я получить доступ к первой строке из s в PL/pgSQL.

Я думал, что PL/pgSQL будет более быстрым/чистым, но, возможно, лучше сделать это клиентский код? Есть ли лучшие способы выполнить этот тип при обновлении статистики?

+1

насчет 'AVG (значение) над (перегородкой по sensor_id порядка Т.С. строк между 200 и предыдущей текущей строки) в качестве avg' –

ответ

0

Я предполагаю, что он не будет сильно медленным, чтобы пересчитать последние 200 записей каждый раз при правильной индексации. Если вы будете делать индекс, как:

CREATE INDEX i_sensor_values ON sensor_values(sensor_id, ts DESC); 

вы сможете получить результаты довольно быстро делает:

SELECT sum("value") -- add more expressions as required 
    FROM sensor_values 
WHERE sensor_id=$1 
ORDER BY ts DESC 
LIMIT 200; 

Вы можете выполнить этот запрос в цикле из PL/pgSQL функции. Если вы в ближайшее время перейдете к 9.3 (или выше), вы также сможете использовать для этой цели LATERAL joins.

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

Это хорошо, чтобы проверить Loose Index scans также.

P.S. Имя столбца value должно быть двойным, так как это SQL reserved word.

+0

Хм делает SELECT, sensor_id, значение ОТ sensor_values ​​ ГДЕ sensor_id = 555 ORDER BY DESC LIMIT Т.С. 200; Принимал более двух минут, поскольку он должен был заказывать все данные. 1 секунда кэширована. http://explain.depesz.com/s/DbN Выполнение SELECT ts, значение FROM sensor_values ​​WHERE sensor_values.sensor_id = 540 И ts> = (NOW() - INTERVAL '2 day') :: timestamptz ORDER BY ts DESC LIMIT 200 принимает 100 мс . Когда я смогу получить тысячу новых записей в минуту, я думаю, что это слишком много накладных расходов. Я думаю, что индексы работают нормально. Возможно, я могу сделать это не в реальном времени каждые 1000 или около того записей. –

+0

@GlennPierce, Вы не упомянули раздел. Конечно, чтобы получить надлежащую разбивку разделов, вам нужно добавить соответствующий предикат. Можете ли вы показать план второго запроса, пожалуйста? – vyegorov

+0

Планирование второго запроса http://explain.depesz.com/s/tXrD Не знаете, почему он должен проверять таблицы разделов на предыдущие годы. –

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