【发布时间】:2011-07-27 13:26:13
【问题描述】:
这个问题让我头疼了一阵子。我有一个 PostgreSQL 8.4 数据库,它只包含一个包含超过 4.000.000 条记录的表。该表的结构如下:
CREATE TABLE metadata (
id serial NOT NULL,
"value" text NOT NULL DEFAULT ''::text,
segment_box box NOT NULL DEFAULT box(point(((-9223372036854775808)::bigint)::double precision, (1)::double precision), point((9223372036854775807::bigint)::double precision, ((-1))::double precision)),
CONSTRAINT metadata_pk PRIMARY KEY (id)
)
CREATE INDEX metadata_segment_box_ix
ON metadata
USING gist
(segment_box);
CREATE INDEX metadata_tag_value_ix
ON metadata
USING btree
(value);
该表包含以矩形框表示的段(按时间)。这些段使用“值”列进行注释。
我想在数据库上执行的那种查询尝试查找包含在某个窗口中的具有指定值的所有段。成功实现这一点的查询是:
SELECT * FROM (SELECT * FROM metadata WHERE value='X') a,
(SELECT * FROM metadata WHERE AND value='Y') b
WHERE a.segment_box <-> b.segment_box <= 3000
但是,您可能已经注意到,数据库无法有效地执行此查询。子查询 a 和 b 的笛卡尔积变得非常大。有没有办法更有效地执行这些查询?我可以想象某种滑动窗口方法可以解决问题。可能类似于以下内容:
SELECT *, rank() OVER (
PARTITION BY "value" ORDER BY (segment_box[1])[0], (segment_box[0])[0]
) FROM metadata WHERE value='X' OR value='Y'
更新: 发布此问题后,我尝试的其中一件事是在 Postgres 中创建自定义函数。我试过了:
CREATE OR REPLACE FUNCTION within_window(size bigint DEFAULT 0)
RETURNS setof metadata AS
$BODY$DECLARE
segment RECORD;
neighbour RECORD;
newwindow box;
BEGIN
FOR segment IN (
SELECT * FROM metadata WHERE value='X' OR value='Y'
ORDER BY (segment_box[1])[0], (segment_box[0])[0]
) LOOP
newwindow := box(segment.segment_box[0],
point((((segment.segment_box[1])[0]) + size), (segment.segment_box[1])[1]));
FOR neighbour IN (
SELECT DISTINCT ON (metadata_id) * FROM metadata WHERE value='X' OR value='Y')
AND segment_box &< newwindow
AND segment_box &> newwindow
) LOOP
RETURN NEXT neighbour;
END LOOP;
END LOOP;
END;$BODY$
LANGUAGE plpgsql;
然而,这个函数和我上面描述的基本解决方案一样慢,因为子查询必须执行多次。对此有何其他想法??
【问题讨论】:
-
您在问题中有
postgis作为标签,但您在这里没有使用 PostGIS。如果你这样做了,你会有像 ST_DWithin 这样的类似缓冲区的函数来帮助你。 -
您认为与答案中提供的函数相比,使用 ST_DWithin 函数会使查询更快吗?
标签: sql database algorithm postgresql postgis