Эта проблема на некоторое время вызывает у меня головную боль. У меня есть база данных 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 становится действительно большим. Есть ли способ выполнить эти запросы более эффективно? Я могу себе представить, что какой-то подход со скользящим окном сработает. Может быть что-то вроде следующего:
* * 1010
Обновление:
После публикации этого вопроса я попытался создать пользовательскую функцию в 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;
Однако эта функция работает медленнее, чем базовое решение, которое я описал выше, из-за подзапроса, который должен выполняться много раз. Любые другие мысли по этому поводу?