PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

Räumliche Abfragen für große Tabellen mit mehreren Selbstverknüpfungen sind langsam

Diese Abfrage sollte sehr weit reichen (viel sein schneller):

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;

Dies ist nicht das gleiche wie Ihre ursprüngliche Abfrage, aber eher das, was Sie tatsächlich wollen, wie in den Kommentaren diskutiert :

Diese Abfrage gibt also eine Liste dieser Schulen zurück, gefolgt von Bars und Restaurants in der Nähe. Jeder Zeilensatz wird durch die osm_id zusammengehalten der Schule in der Spalte school_id .

Jetzt mit LATERAL Joins, um den räumlichen GiST-Index zu nutzen.

TABLE school ist nur eine Abkürzung für SELECT * FROM school :

Der Ausdruck (type <> 'school') ordnet die Schule in jedem Satz zuerst an, weil:

Die Unterabfrage sub im abschließenden SELECT wird nur benötigt, um nach diesem Ausdruck zu ordnen. Eine UNION Abfrage schränkt einen angehängten ORDER BY ein listet nur Spalten auf, keine Ausdrücke.

Ich konzentriere mich auf die Frage, die Sie für den Zweck dieser Antwort gestellt haben - Ignorieren die erweiterte Anforderung, nach einer der anderen 70 Textspalten zu filtern. Das ist wirklich ein Konstruktionsfehler. Die Suchkriterien sollten auf wenige konzentriert werden Säulen. Oder Sie müssen alle 70 Spalten indizieren, und mehrspaltige Indizes, wie ich sie vorschlagen werde, sind kaum eine Option. Immer noch möglich obwohl ...

Index

Zusätzlich zu den bestehenden:

"idx_planet_osm_point_waygeo" gist (way_geo)

Wenn Sie immer nach derselben Spalte filtern, können Sie einen erstellen mehrspaltiger Index die wenigen Spalten abdecken, an denen Sie interessiert sind, also index- scannt nur möglich werden:

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

Postgres 9.5

Das kommende Postgres 9.5 führt wichtige Verbesserungen ein die zufällig genau Ihren Fall ansprechen:

Das ist für Sie besonders interessant. Jetzt können Sie eine Single haben mehrspaltiger (überdeckender) GiST-Index:

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

Und:

Und:

Wieso den? Weil ROLLUP würde die von mir vorgeschlagene Abfrage vereinfachen. Zugehörige Antwort:

Die erste Alpha-Version wurde am 2. Juli 2015 veröffentlicht. Der erwartete Zeitplan für die Veröffentlichung:

Grundlagen

Achten Sie natürlich darauf, die Grundlagen nicht zu übersehen: