PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

Requête spatiale sur une grande table avec plusieurs auto-jointures s'exécutant lentement

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 :