优化函数,它提取时间戳中具有最小间隙的记录
Optimising function which extracts records with a minimum gap in timestamps
我在 Postgres 9.4.5 中有大量 table 时间戳:
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 分钟的每一行。我已经使用 LAG()
尝试了几个不同的 SELECT
语句,它们都有效,但没有给我所需的确切结果。下面的函数给了我我需要的,但我觉得它可以更快:
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 = AND vp.timestamp BETWEEN ::timestamp AND ::timestamp;
BEGIN
FOR row IN tbl
LOOP
count := coalesce(row.diff,0) + count;
IF count >= *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
语句中,我已经可以访问要保留的每一行,因此可以将它们存储在临时 table 中,然后在最后 return 临时 table循环。
是否可以优化此功能 - 尤其是提高性能?
查询
您的函数有各种昂贵的、不必要的开销。单个查询应该快很多倍,做同样的事情:
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 =
AND vp.timestamp >= -- typically you'd include the lower bound
AND vp.timestamp < ; -- ... and exlude the upper
ORDER BY posid
) sub
WHERE diff >= ;
END
$func$ LANGUAGE plpgsql STABLE;
也可以只是一个 SQL 函数或没有任何包装的裸 SELECT
(也许是准备好的语句?)
请注意 starttime
和 endtime
是如何作为 timestamp
传递的。 (作为 text
传递并强制转换是没有意义的。)最小间隔 min_interval
是一个实际的 interval
。通过您选择的 任意 间隔。
索引
如果 mmsi
上的谓词在任何方面都是选择性的,您当前 拥有的两个索引 (PK ON (posid, mmsi)
和 idx on (timestamp)
) 不是很有用。如果将 PK 的列顺序反转为 (mmsi, posid)
,它对于手头的查询会变得更加有用。参见:
最佳索引通常在 vessel_position(mmsi, timestamp)
。相关:
旁白:避免将 keywords 作为标识符。那是自找麻烦。另外,实际上包含 timestamptz
的列 timestamp
具有误导性。
我在 Postgres 9.4.5 中有大量 table 时间戳:
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 分钟的每一行。我已经使用 LAG()
尝试了几个不同的 SELECT
语句,它们都有效,但没有给我所需的确切结果。下面的函数给了我我需要的,但我觉得它可以更快:
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 = AND vp.timestamp BETWEEN ::timestamp AND ::timestamp;
BEGIN
FOR row IN tbl
LOOP
count := coalesce(row.diff,0) + count;
IF count >= *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
语句中,我已经可以访问要保留的每一行,因此可以将它们存储在临时 table 中,然后在最后 return 临时 table循环。
是否可以优化此功能 - 尤其是提高性能?
查询
您的函数有各种昂贵的、不必要的开销。单个查询应该快很多倍,做同样的事情:
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 =
AND vp.timestamp >= -- typically you'd include the lower bound
AND vp.timestamp < ; -- ... and exlude the upper
ORDER BY posid
) sub
WHERE diff >= ;
END
$func$ LANGUAGE plpgsql STABLE;
也可以只是一个 SQL 函数或没有任何包装的裸 SELECT
(也许是准备好的语句?
请注意 starttime
和 endtime
是如何作为 timestamp
传递的。 (作为 text
传递并强制转换是没有意义的。)最小间隔 min_interval
是一个实际的 interval
。通过您选择的 任意 间隔。
索引
如果 mmsi
上的谓词在任何方面都是选择性的,您当前 拥有的两个索引 (PK ON (posid, mmsi)
和 idx on (timestamp)
) 不是很有用。如果将 PK 的列顺序反转为 (mmsi, posid)
,它对于手头的查询会变得更加有用。参见:
最佳索引通常在 vessel_position(mmsi, timestamp)
。相关:
旁白:避免将 keywords 作为标识符。那是自找麻烦。另外,实际上包含 timestamptz
的列 timestamp
具有误导性。