Cette requête devrait aller loin (être beaucoup plus rapide):
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;
Ce n'est pas la même chose que votre requête d'origine, mais plutôt ce que vous voulez réellement, selon la discussion dans les commentaires :
Cette requête renvoie donc une liste de ces écoles, suivies des bars et restaurants à proximité. Chaque ensemble de lignes est maintenu ensemble par le osm_id
de l'école dans la colonne school_id
.
Utilise maintenant LATERAL
jointures, pour utiliser l'index spatial GiST.
TABLE school
est juste un raccourci pour SELECT * FROM school
:
L'expression (type <> 'school')
ordonne l'école dans chaque ensemble en premier, car :
La sous-requête sub
dans le dernier SELECT
est seulement nécessaire de commander par cette expression. Un UNION
la requête limite un ORDER BY
attaché liste uniquement les colonnes, pas d'expressions.
Je me concentre sur la requête que vous avez présentée aux fins de cette réponse - ignorer l'exigence étendue de filtrer sur l'une des 70 autres colonnes de texte. C'est vraiment un défaut de conception. Les critères de recherche doivent être concentrés sur quelques Colonnes. Ou vous devrez indexer les 70 colonnes, et les index multicolonnes comme je vais le proposer ne sont guère une option. Toujours possible quoique...
Index
En plus de l'existant :
"idx_planet_osm_point_waygeo" gist (way_geo)
Si vous filtrez toujours sur la même colonne, vous pouvez créer un index multicolonne couvrant les quelques colonnes qui vous intéressent, donc index- scanne uniquement deviennent possibles :
CREATE INDEX planet_osm_point_bar_idx ON planet_osm_point (amenity, name, osm_id)
Postgres 9.5
Le prochain Postgres 9.5 introduit des améliorations majeures qui s'avèrent justement répondre à votre cas :
Cela vous intéresse particulièrement. Maintenant, vous pouvez avoir un célibataire Index GiST multicolonne (couvrant) :
CREATE INDEX reservations_range_idx ON reservations
USING gist(amenity, way_geo, name, osm_id)
Et :
Et :
Pourquoi? Parce que ROLLUP
simplifierait la requête que j'ai suggérée. Réponse connexe :
La première version alpha a été publiée le 2 juillet 2015. Le calendrier prévu pour la publication :
Bases
Bien sûr, veillez à ne pas négliger les bases :