¿Cómo solucionar un problema de rendimiento en PostGIS ST_Intersects?


9

Soy un novato en postgis y tengo un problema en el rendimiento de las consultas.

Esta es mi consulta:

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

y el problema es que mi multipolígono incluye polígonos MUY grandes (¡600 páginas de largo en word doc!) y tardó más de 2 horas en ejecutarse!

¿Hay alguna forma de optimizar mi consulta o usar otra forma?

Por favor, su ayuda es muy apreciada!

Respuestas:


8

Lo que debe hacer es colocar su gran multipolígono en una tabla como polígonos individuales (con ST_Dump) y poner un índice sobre él. Algo como:

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;

Eso debería ser mucho más rápido por varias razones.


Gracias Nicklas por esta gran respuesta. Lamento no mencionar que tengo más de un polígono y ya están almacenados en una tabla con índice. Pero pensé que proporcionar los datos geom directamente sería más rápido. Sin embargo, intento de la manera que sugieres, ¡pero todavía me lleva mucho tiempo! alguna otra sugerencia?
Sara

@Sara. Ok, ¿entonces trataste de dividir las multigeoetrías en geometrías individuales como sugiero con ST_Dump?
Nicklas Avén

¿De cuántas posiciones de usuarios estamos hablando? ¿Cuántos polígonos grandes? ¿Qué obtienes de SELECT ST_npoints (geom) de big_polygons_table ;?
Nicklas Avén

Perdón, déjame explicarte más sobre mis tablas para que te quede más claro: tengo la tabla1 que incluye una columna geom que tiene aproximadamente 230 filas y en cada fila hay un multipolígono (representan países, por lo que varían en tamaño) y tener índice en the_geom col. Tabla2 que incluye la columna de posición (puntos), marca de tiempo, ID de usuario e ID (pk) y 3 índices creados usando (posición, marca de tiempo, ID de usuario). Esta tabla es muy grande, aproximadamente 103496003 filas. El número máximo de ST_npoints es 1440430 y el número mínimo es 16. Lo siento si te hice confundir, ¡pero realmente necesito tu ayuda! Gracias
Sara

2

Depende de qué tipo de calidad: precisión necesita. Obviamente, puede simplificar los polígonos usando: http://postgis.net/docs/ST_Simplify.html

Lo que hice a menudo durante el desarrollo de mi aplicación SIG fue pensar en la mejor manera de minimizar los datos. P.ej. preseleccione los polígonos dentro del cuadro de límite, por ejemplo. - Dependiendo del nivel de zoom, no necesita resultados ultra precisos (st_simplify), etc.

¡Espero que te haya ayudado un poco!


Gracias Martin por tu rápida respuesta. Mi problema es que necesito que el resultado sea muy preciso, ¡así que creo que esta función no me ayudará aquí! pero gracias por la sugerencia
Sara

0

Dependiendo de su experiencia en postgres y / o sql, tiene varias opciones:

  1. analice la consulta a través del comando EXPLICAR para averiguar si está llegando a un cuello de botella en particular. Advertencia: a veces la salida de EXPLAIN puede ser difícil de entender

  2. si espera que la mayoría o una parte significativa de las geometrías en la tabla 1 NO se crucen con el multipolígono, puede intentar aplicar una condición preliminar contra un polígono más simple (es decir, al romper el multiploygon en piezas más pequeñas) y luego ejecutar la intersección multipolígono más pesada solo en esos resultados Vea a continuación un ejemplo.

  3. si y sólo si la CPU es el cuello de botella (es decir, el servidor es intersecciones de computación atascados) Me debidamente sugiero que conseguir una más grande, más rápido, CPU más potente o alquilar una sola vez de alta CPU Instancia fuera EC2 de Amazon y lo destruyen cuando estás hecho

Ejemplo de consulta para el ítem 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

Para mejorar el rendimiento, también podría materializar temporalmente la subselección st1 como una tabla para poder indexarla.

@Nicklas tiene razón al señalar en los comentarios que el ejemplo de la sugerencia 2 no debería ayudar. Tiene razón, pero creo que yo también estoy (en parte) en lo cierto.

De hecho, parece que se hizo una pregunta muy similar (y se respondió) en noviembre pasado sobre el ML postgis:

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

y resulta que la sugerencia es dividir realmente el polígono para que el índice pueda filtrar de manera más efectiva las intersecciones falsas que de otra manera serían activadas por una simple verificación de límites.


la sugerencia 2 no debería ayudar porque eso es exactamente lo que está haciendo el índice. Así que esa construcción hará lo mismo una vez más.
Nicklas Avén

@ NicklasAvén tienes razón, modifiqué la respuesta
unicoletti

0

Utilizando ST_SubDivide()

Para la versión 2.2 de Postgis, puede usar ST_SubDivide.

ST_Subdivide : devuelve un conjunto de geometría donde ninguna geometría del conjunto tiene más que el número especificado de vértices.

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

Puede también

  • usar una tabla temporal
  • un índice

Aquí usamos ST_SubDividepara dividir el polígono en subpolígonos con 10 o menos vértices.

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

CREATE INDEX divided_idx ON divided USING gist(geom);

Entonces

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;

No haga lo anterior, presenta errores de redondeo

Afinación general

También mire la sección titulada Consejos de rendimiento en los documentos. Asegúrate de estar sintonizado adecuadamente. Considere subir max_parallel_workers_per_gatherpara aprovechar la paralelización (actualmente, por defecto, está desactivado).

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.