29. Närmaste granne-sökning

29.2. Närmaste granne Join

Den indexassisterade order by-operatorn har en stor nackdel: den fungerar bara med en enkel geometrilitteral på ena sidan av operatorn. Detta är bra för att hitta de objekt som ligger närmast ett frågeobjekt, men hjälper inte för en spatial join, där målet är att hitta den närmaste grannen för var och en av en fullständig uppsättning kandidater.

Lyckligtvis finns det en funktion i SQL-språket som gör att vi kan köra en fråga upprepade gånger i en slinga: LATERAL join.

Här hittar vi den närmaste gatan till varje tunnelbanestation:

SELECT subways.gid AS subway_gid,
       subways.name AS subway,
       streets.name AS street,
       streets.gid AS street_gid,
       streets.geom::geometry(MultiLinestring, 26918) AS street_geom,
       streets.dist
FROM nyc_subway_stations subways
CROSS JOIN LATERAL (
  SELECT streets.name, streets.geom, streets.gid, streets.geom <-> subways.geom AS dist
  FROM nyc_streets AS streets
  ORDER BY dist
  LIMIT 1
) streets;

Observera hur CROSS JOIN LATERAL fungerar som den inre delen av en slinga som drivs av tunnelbanetabellen. Varje post i tunnelbanetabellen matas in i den laterala underfrågan, en i taget, så att du får ett närmaste resultat för varje tunnelbanepost.

_images/knn4.png

Förklaringen visar slingan på tunnelbanestationerna och den indexassisterade beställningen genom att lägga in slingan där vi vill ha den:

                           QUERY PLAN
-------------------------------------------------------------------------
 Nested Loop  (cost=0.28..13140.71 rows=491 width=37)
   ->  Seq Scan on nyc_subway_stations subways
       (cost=0.00..15.91 rows=491 width=46)
   ->  Limit
       (cost=0.28..1.71 rows=1 width=170)
         ->  Index Scan using nyc_streets_geom_idx on nyc_streets streets
             (cost=0.28..27410.12 rows=19091 width=170)
                Order By: (geom <-> subways.geom)