PostgreSQL
 sql >> Database >  >> RDS >> PostgreSQL

Query spaziale su una tabella di grandi dimensioni con più auto join che funzionano lentamente

Questa query dovrebbe fare molto (essere molto più veloce):

WITH school AS (
   SELECT s.osm_id AS school_id, text 'school' AS type, s.osm_id, s.name, s.way_geo
   FROM   planet_osm_point s
        , LATERAL (
      SELECT  1 FROM planet_osm_point
      WHERE   ST_DWithin(way_geo, s.way_geo, 500, false)
      AND     amenity = 'bar'
      LIMIT   1  -- bar exists -- most selective first if possible
      ) b
        , LATERAL (
      SELECT  1 FROM planet_osm_point
      WHERE   ST_DWithin(way_geo, s.way_geo, 500, false)
      AND     amenity = 'restaurant'
      LIMIT   1  -- restaurant exists
      ) r
   WHERE  s.amenity = 'school'
   )
SELECT * FROM (
   TABLE school  -- schools

   UNION ALL  -- bars
   SELECT s.school_id, 'bar', x.*
   FROM   school s
        , LATERAL (
      SELECT  osm_id, name, way_geo
      FROM    planet_osm_point
      WHERE   ST_DWithin(way_geo, s.way_geo, 500, false)
      AND     amenity = 'bar'
      ) x

   UNION ALL  -- restaurants
   SELECT s.school_id, 'rest.', x.*
   FROM   school s
        , LATERAL (
      SELECT  osm_id, name, way_geo
      FROM    planet_osm_point
      WHERE   ST_DWithin(way_geo, s.way_geo, 500, false)
      AND     amenity = 'restaurant'
      ) x
   ) sub
ORDER BY school_id, (type <> 'school'), type, osm_id;

Questo è non lo stesso della tua query originale, ma piuttosto quello che vuoi effettivamente, come da discussione nei commenti :

Quindi questa query restituisce un elenco di quelle scuole, seguito da bar e ristoranti nelle vicinanze. Ogni insieme di righe è tenuto insieme da osm_id della scuola nella colonna school_id .

Ora usando LATERAL join, per utilizzare l'indice GiST spaziale.

TABLE school è solo un'abbreviazione per SELECT * FROM school :

L'espressione (type <> 'school') ordina prima la scuola in ogni set, perché:

La sottoquery sub nel SELECT finale è necessario solo per ordinare con questa espressione. Un UNION la query limita un ORDER BY allegato elenco solo in colonne, nessuna espressione.

Mi concentro sulla domanda che hai presentato ai fini di questa risposta:ignorare il requisito esteso di filtrare su una qualsiasi delle altre 70 colonne di testo. Questo è davvero un difetto di progettazione. I criteri di ricerca dovrebbero essere concentrati in pochi colonne. Oppure dovrai indicizzare tutte le 70 colonne e gli indici multicolonna come ho intenzione di proporre non sono certo un'opzione. Ancora possibile però...

Indice

Oltre all'esistente:

"idx_planet_osm_point_waygeo" gist (way_geo)

Se si filtra sempre sulla stessa colonna, è possibile creare un indice multicolonna coprendo le poche colonne che ti interessano, quindi index- solo scansioni diventa possibile:

CREATE INDEX planet_osm_point_bar_idx ON planet_osm_point (amenity, name, osm_id)

Postgres 9.5

Il prossimo Postgres 9.5 introduce importanti miglioramenti che accada per affrontare esattamente il tuo caso:

Questo è di particolare interesse per te. Ora puoi avere un single indice GiST a più colonne (a copertura):

CREATE INDEX reservations_range_idx ON reservations
USING gist(amenity, way_geo, name, osm_id)

E:

E:

Come mai? Perché ROLLUP semplificherebbe la query che ho suggerito. Risposta correlata:

La prima versione alpha è stata rilasciata il 2 luglio 2015. La sequenza temporale prevista per il rilascio:

Nozioni di base

Ovviamente, assicurati di non trascurare le basi: