Оптимизирующая функция, которая извлекает записи с минимальным промежутком времени - PullRequest
1 голос
/ 16 мая 2019

У меня есть большая таблица меток времени в Postgres 9.4.5:

CREATE TABLE vessel_position (
  posid serial NOT NULL,
  mmsi integer NOT NULL,
  "timestamp" timestamp with time zone,
  the_geom geometry(PointZ,4326),
  CONSTRAINT "PK_posid_mmsi" PRIMARY KEY (posid, mmsi)
);

Дополнительный индекс:

CREATE INDEX vessel_position_timestamp_idx ON vessel_position ("timestamp");

Я хочу извлечь каждую строку, где метка времени не меньше xминут после предыдущего ряда.Я пробовал несколько различных SELECT операторов, используя LAG(), которые все работали, но не дали мне точного результата, который мне нужен.Приведенные ниже функции дают мне то, что мне нужно, но я чувствую, что это может быть быстрее:

CREATE OR REPLACE FUNCTION _getVesslTrackWithInterval(mmsi integer, startTime character varying (25) ,endTime character varying (25), interval_min integer)
RETURNS SETOF vessel_position AS
$func$
DECLARE
    count integer DEFAULT 0;
    posids varchar DEFAULT '';
    tbl CURSOR FOR
    SELECT
      posID
      ,EXTRACT(EPOCH FROM (timestamp -  lag(timestamp) OVER (ORDER BY posid asc)))::int as diff
    FROM vessel_position vp WHERE vp.mmsi = $1  AND vp.timestamp BETWEEN $2::timestamp AND $3::timestamp;
BEGIN
FOR row IN tbl
LOOP
    count := coalesce(row.diff,0) + count;
    IF count >= $4*60 OR count = 0 THEN
            posids:= posids || row.posid || ',';
            count:= 0;
     END IF;
END LOOP;
RETURN QUERY EXECUTE 'SELECT * from vessel_position where posid in (' || TRIM(TRAILING ',' FROM posids) || ')';
END
$func$ LANGUAGE plpgsql;

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

Может ли эта функция бытьоптимизирован - для повышения производительности, в частности?

1 Ответ

2 голосов
/ 17 мая 2019

Запрос

Ваша функция имеет все виды дорогостоящих, ненужных накладных расходов.Один запрос должен быть во много раз быстрее , делая то же самое:

CREATE OR REPLACE FUNCTION _get_vessel_track_with_interval
 (mmsi int, starttime timestamptz, endtime timestamptz, min_interval interval)
  RETURNS SETOF vessel_position AS
$func$
BEGIN
   SELECT (vp).*  -- parentheses required for decomposing row type
   FROM  (
      SELECT vp   -- whole row (!)
           , timestamp - lag(timestamp) OVER (ORDER BY posid) AS diff
      FROM   vessel_position vp
      WHERE  vp.mmsi = $1
      AND    vp.timestamp >= $2     -- typically you'd include the lower bound
      AND    vp.timestamp <  $3;    -- ... and exlude the upper
      ORDER  BY posid
      ) sub
   WHERE  diff >= $4;
END
$func$  LANGUAGE plpgsql STABLE;

Может также быть просто функцией SQL или «голым» SELECT без какой-либо оболочки(Может быть, как подготовленное утверждение? Пример. )

Обратите внимание, как starttime и endtime передаются как timestamp.(Не имеет смысла передавать как text и разыгрывать.) А минимальный интервал min_interval является действительным interval.Передайте любой интервал по вашему выбору.

Индекс

Если предикат на mmsi каким-либо образом избирательный, два индекса, которые у вас на данный момент имеют (PK ON (posid, mmsi) и idx на (timestamp)) не очень полезны.Если вы измените порядок столбцов вашего PK на (mmsi, posid), это станет гораздо более полезным для рассматриваемого запроса.См .:

Оптимальный индекс для этого обычно будет vessel_position(mmsi, timestamp).Связанные:

В стороне: избегайте ключевых слов в качестве идентификаторов.Это напрашивается на неприятности.Кроме того, столбец timestamp, который фактически содержит timestamptz, вводит в заблуждение.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...