在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
语句中,我已经可以访问要保留的每一行,因此可以将它们存储在临时表中,然后在循环结束时返回临时表。
可以优化此功能-特别是提高性能吗?
答案 0 :(得分:2)
您的函数具有各种昂贵的不必要的开销。单个查询应该 快许多倍 ,执行相同的操作:
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
(也许是准备好的语句?Example.)
请注意starttime
和endtime
如何作为timestamp
传递。 (以text
的形式传递并强制转换。)最小间隔min_interval
是实际的interval
。传递您选择的任意间隔。
如果mmsi
上的谓词具有选择性,那么您当前拥有的两个索引(PK ON (posid, mmsi)
和idx on (timestamp)
)不会很有用。如果将PK的列顺序颠倒为(mmsi, posid)
,则对于手头的查询而言,它将变得更加有用。参见:
为此的最佳索引通常在vessel_position(mmsi, timestamp)
上。相关:
在旁边:避免使用keywords作为标识符。那是麻烦。另外,实际上包含timestamp
的列timestamptz
具有误导性。