Как исправить проблему с производительностью в PostGIS ST_Intersects?

9

Я новичок в postgis, и у меня проблема с выполнением запросов.

Это мой запрос:

SELECT DISTINCT ON (userid) userid ,ST_AsText(position), timestamp  
FROM table1 
WHERE ST_Intersects ( ST_GeomFromText('a multiypolygon geom goes here',4326),position) 
ORDER BY userid, timestamp desc

и проблема в том, что мой мультиполигон включает ОЧЕНЬ большие полигоны (600 страниц длиной в word doc!), и выполнение заняло более 2 часов!

Есть ли способ оптимизировать мой запрос или использовать другой способ?

Пожалуйста, ваша помощь очень ценится!

Сара
источник

Ответы:

8

Что вам нужно сделать, это поместить ваш большой мультиполигон в таблицу как отдельные полигоны (с ST_Dump) и поместить в него индекс. Что-то вроде:

CREATE TABLE big_polygon as
SELECT (ST_Dump( ST_GeomFromText('a multiypolygon geom goes here',4326))).geom as geom;

-- It is always great to put a primary key on the table
ALTER table big_polygon ADD Column gid serial PRIMARY KEY;

-- Create the index
CREATE INDEX idx_big_polygon_geom
on big_polygon
USING gist(geom);

-- To give the database some information about how the index looks
analyze big_polygon;

-- Then you go:
SELECT DISTINCT ON (userid) userid ,ST_AsText(position), timestamp  
FROM table1, big polygon WHERE ST_Intersects ( big_polygon.geom,position) 
ORDER BY userid, timestamp desc;

Это должно быть намного быстрее по нескольким причинам.

Никлас Авен
источник
Спасибо Никласу за этот отличный ответ. Извините, я упустил упомянуть, что у меня есть более одного полигона, и они уже хранятся в таблице с индексом. Но я старался, чтобы данные геом напрямую были быстрее. Тем не менее, я стараюсь, как вы предлагаете, но это все еще занимает очень много времени! любое другое предложение?
Сара
@ Сара. Итак, вы попытались разделить мультигеоетрии на отдельные геометрии, как я советую с ST_Dump?
Никлас Авен
Сколько пользовательских позиций мы говорим? Сколько больших полигонов? Что вы получаете от SELECT ST_npoints (geom) от big_polygons_table ;?
Никлас Авен
Извините, мой плохой, позвольте мне объяснить больше о моих таблицах, чтобы вам было более понятно: у меня есть table1, который включает в себя столбец geom, имеющий около 230 строк, и в каждой строке есть мультиполигон (они представляют страны, поэтому они различаются по размеру) и иметь индекс в столбце the_geom. Таблица 2, которая включает в себя столбец положения (точки), метку времени, идентификатор пользователя и идентификатор (pk) и 3 индекса, созданные с использованием (позиция, метка времени, идентификатор пользователя). Эта таблица очень велика, примерно 103496003 строк. Макс. Число ST_npoints равно 1440430, а минимальное число равно 16. Извините, если я вас запутал, но мне очень нужна ваша помощь! Спасибо
Сара
2

Это зависит от того, какое качество - точность вам нужна. Очевидно, что вы можете упростить полигоны, используя: http://postgis.net/docs/ST_Simplify.html

Во время разработки моего ГИС-приложения я часто думал о том, как лучше минимизировать данные. Например. например, предварительно выберите полигоны в граничной рамке. - В зависимости от уровня масштабирования вам не нужны сверхточные результаты (st_simplify), так что ...

Надеюсь, что это помогло вам немного!

STYP
источник
Спасибо Мартину за ваш быстрый ответ. Моя проблема в том, что мне нужен очень точный результат, поэтому я думаю, что эта функция мне здесь не поможет! но спасибо за предложение
Сара
0

В зависимости от вашего опыта postgres и / или sql у вас есть несколько вариантов:

  1. проанализируйте запрос с помощью команды EXPLAIN, чтобы выяснить, не достигли ли вы определенного узкого места. Предупреждение: иногда вывод EXPLAIN может быть трудным для понимания

  2. если вы ожидаете, что большинство или значительная часть геометрий в таблице 1 НЕ пересекают мультиполигон, вы можете попытаться применить предварительное условие к более простому многоугольнику (то есть, разбив мультиполигон на более мелкие части), а затем запустить более тяжелое пересечение мультиполигона только на эти результаты. Смотрите ниже пример.

  3. если и только если ЦП является узким местом (т. е. сервер застрял на пересечениях), я настоятельно рекомендую вам получить больший, более быстрый и более мощный ЦП или арендовать одноразовый экземпляр с высоким ЦП у Amazon EC2 и уничтожить его, когда вы сделанный

Пример запроса для позиции 2:

SELECT DISTINCT ON (st1.userid) st1.userid ,ST_AsText(st1.position), st1.timestamp  
FROM (
    select userid, position, timestamp from table1 
    WHERE ST_Intersects ( YOUR_MULTIPOL_BOUNDS_HERE,position)
) as st1 
WHERE ST_Intersects ( ST_GeomFromText('a multiypolygon geom goes     here',4326),st1.position) 
ORDER BY st1.userid, st1.timestamp desc

Для повышения производительности вы также можете временно материализовать подвыбор st1 в виде таблицы, чтобы ее можно было проиндексировать.

@Nicklas правильно указал в комментариях, что пример для предложения 2 не должен помочь. Он прав, но я думаю, что я (частично) тоже прав.

На самом деле, кажется, очень похожий вопрос был задан (и получен ответ) только в ноябре прошлого года на postgis ML:

http://postgis.refractions.net/pipermail/postgis-users/2011-November/031344.html

и оказывается, что предложение состоит в том, чтобы фактически разбить многоугольник, чтобы индекс мог наиболее эффективно отфильтровывать ложные пересечения, которые в противном случае были бы инициированы простой проверкой границы.

unicoletti
источник
Предложение 2 не должно помочь, потому что это именно то, что делает индекс. Так что конструкция будет делать то же самое еще раз.
Никлас Авен
@ NicklasAvén ты прав, я исправил ответ
unicoletti
0

С помощью ST_SubDivide()

Для версии 2.2 Postgis, вы можете использовать ST_SubDivide.

ST_Subdivide - возвращает набор геометрии, где ни одна геометрия в наборе не имеет больше указанного количества вершин.

setof geometry ST_Subdivide(geometry geom, integer max_vertices=256);

Вы также можете

  • использовать временную таблицу
  • индекс

Здесь мы используем ST_SubDivideдля разбиения многоугольника на подполигоны с 10 или менее вершинами.

CREATE TEMP TABLE divided AS
SELECT ST_SubDivide(bigmultipolygon,10)::geometery AS t(geom);

CREATE INDEX divided_idx ON divided USING gist(geom);

затем

SELECT DISTINCT ON (userid) userid ,ST_AsText(position), timestamp  
FROM table1
JOIN divided AS d
  ON ST_Intersects( d.geom, position )
ORDER BY userid, timestamp desc;

Не делайте выше, это приводит к ошибкам округления

Генеральный Тюнинг

Также ознакомьтесь с разделом « Советы по повышению эффективности» в документации. Убедитесь, что вы настроены соответствующим образом. Рассмотрите возможность повышения, max_parallel_workers_per_gatherчтобы воспользоваться преимуществами распараллеливания (в настоящее время по умолчанию отключено).

Эван Кэрролл
источник