29. Vecinos cercanos

29.2. Join Vecino más próximo

El operador de ordenación por índice tiene un inconveniente importante: sólo funciona con un único literal geométrico en un lado del operador. Esto está bien para encontrar los objetos más cercanos a un objeto definido, pero no ayuda para un JOIN espacial, donde el objetivo es encontrar el vecino más cercano para cada uno de los objetos de un conjunto completo de candidatos.

Afortunadamente, existe una característica del lenguaje SQL que nos permite ejecutar una consulta en un bucle: el LATERAL join.

Aquí encontrará la calle más cercana a cada estación de metro:

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;

Observe cómo el CROSS JOIN LATERAL actúa como la parte interna de un bucle controlado por la tabla subways. Cada registro de la tabla subways se introduce en la subconsulta lateral, de uno en uno, de forma que se obtiene un resultado más cercano para cada registro de metro.

_images/knn4.png

La explicación muestra el bucle en las estaciones de metro, y el orden asistido por índices dentro del bucle donde queramos:

                           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)