Come risolvere il problema di prestazioni in PostGIS ST_Intersects?


9

Sono un principiante in Postgis e ho un problema con le prestazioni della query.

Questa è la mia domanda:

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

e il problema è che il mio multipoligono include MOLTO grandi poligoni (600 pagine lunghe in word doc!) e ci sono volute più di 2 ore per l'esecuzione!

C'è un modo per ottimizzare la mia query o utilizzare un altro modo?

Per favore, il tuo aiuto è molto apprezzato!

Risposte:


8

Quello che dovresti fare è mettere il tuo grande multipoligono in una tabella come singoli poligoni (con ST_Dump) e inserirvi un indice. Qualcosa di simile a:

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;

Dovrebbe essere molto più veloce per diversi motivi.


Grazie Nicklas per questa ottima risposta. Mi dispiace di aver dimenticato di menzionare che ho più di un poligono e sono già memorizzati in una tabella con indice. Ma avrei dovuto fornire i dati geom direttamente più veloci. Tuttavia, provo come suggerisci, ma ci vuole ancora molto tempo! qualche altro suggerimento?
Sara,

@Sara. Ok, quindi hai provato a dividere le multigeoemtries in singole geometrie come suggerisco con ST_Dump?
Nicklas Avén,

Di quante posizioni utente stiamo parlando? Quanti grandi poligoni? Cosa ottieni da SELECT ST_npoints (geom) da big_polygons_table ;?
Nicklas Avén,

Scusami male, lascia che ti spieghi di più sulle mie tabelle per renderti più chiaro: ho table1 che include una colonna geom che ha circa 230 righe e in ogni riga c'è un multipoligono (rappresentano i paesi quindi variano di dimensioni) e hanno indice in the_geom col. Tabella2 che include colonna posizione (punti), data / ora, ID utente e ID (pagina) e 3 indici creati utilizzando (posizione, data / ora, ID utente) .Questa tabella è molto grande circa 103496003 righe Il numero massimo di punti ST è 1440430 e il numero minimo è 16. Mi dispiace se ti ho fatto confondere, ma ho davvero bisogno del tuo aiuto! Grazie
Sara,

2

Dipende dal tipo di qualità: la precisione di cui hai bisogno. Puoi ovviamente semplificare i poligoni usando: http://postgis.net/docs/ST_Simplify.html

Quello che ho fatto spesso durante lo sviluppo della mia applicazione GIS era pensare al modo migliore per ridurre al minimo i dati. Per esempio. preselezionare i poligoni all'interno del riquadro per esempio. - A seconda del livello di zoom non hai bisogno di risultati ultra precisi (st_simplify) così via ...

Spero che ti abbia aiutato un po '!


Grazie Martin per la tua rapida risposta. Il mio problema è che ho bisogno che il risultato sia molto preciso, quindi penso che questa funzione non mi aiuti qui! ma grazie per il suggerimento
Sara

0

A seconda delle tue competenze postgres e / o sql hai diverse opzioni:

  1. analizza la query tramite il comando EXPLAIN per scoprire se stai colpendo un particolare collo di bottiglia. Attenzione: a volte l'output di EXPLAIN può essere difficile da capire

  2. se ti aspetti che la maggior parte o una parte significativa delle geometrie nella tabella1 NON intersechi il multipoligono, potresti provare ad applicare una condizione preliminare contro un poligono più semplice (cioè rompendo il multiploygon in pezzi più piccoli) e quindi eseguire l'intersezione multipoligono più pesante solo su quei risultati. Vedi sotto per un esempio.

  3. se e solo se la CPU è il collo di bottiglia (vale a dire che il server è bloccato nel calcolo delle intersezioni) ti suggerisco caldamente di ottenere una CPU più grande, più veloce, più potente o noleggiare un'istanza a CPU elevata una volta sola da EC2 di Amazon e distruggerla quando sei fatto

Query di esempio per l'articolo 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

Per migliorare le prestazioni, puoi anche materializzare temporaneamente la selezione di st1 come tabella in modo da poterla indicizzare.

@Nicklas ha ragione a sottolineare nei commenti che l'esempio per il suggerimento 2 non dovrebbe aiutare. Ha ragione, ma penso di aver (parzialmente) anche ragione.

In effetti sembra che una domanda molto simile sia stata posta (e risposta) proprio lo scorso novembre sul postgis ML:

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

e risulta che il suggerimento è quello di scomporre effettivamente il poligono in modo che l'indice possa filtrare in modo più efficace false intersezioni che sarebbero altrimenti innescate da un semplice controllo al contorno.


il suggerimento 2 non dovrebbe aiutare perché è esattamente ciò che sta facendo l'indice. Quindi quel costrutto farà di nuovo lo stesso.
Nicklas Avén,

@ NicklasAvén hai ragione, ho modificato la risposta
unicoletti

0

utilizzando ST_SubDivide()

Per la versione 2.2 di Postgis, puoi usare ST_SubDivide.

ST_Subdivide : restituisce un set di geometria in cui nessuna geometria nel set ha un numero di vertici superiore al numero specificato.

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

Puoi anche

  • usa una tabella temporanea
  • un indice

Qui usiamo ST_SubDivideper scomporre il poligono in subpoligoni con 10 o meno vertici.

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

CREATE INDEX divided_idx ON divided USING gist(geom);

Poi

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;

Non fare quanto sopra, introduce errori di arrotondamento

Accordatura generale

Guarda anche la sezione intitolata Suggerimenti sul rendimento nei documenti. Assicurati di essere sintonizzato in modo appropriato. Prendi in considerazione l'idea max_parallel_workers_per_gatherdi rilanciare per sfruttare la parallelizzazione (l'impostazione predefinita è attualmente disattivata).

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.