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: