29. 最近傍探索

29.2. 最近傍結合

インデックスによる ORDER BY 演算子は、演算子の一方が**単一ジオメトリリテラル**でないと動作しないという、大きな欠点があります。これはあるクエリオブジェクトに最も近いオブジェクトを探索する場合は良いのですが、候補から一つずつオブジェクトを抜き出して最近傍を検索することを目的とする空間結合には役立ちません。

幸運なことに、ループ内で繰り返し駆動されるクエリの実行が可能になる SQL 言語機能 LATERAL join があります。

地下鉄駅ごとに最も近いストリートを探索します:

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;

CROSS JOIN LATERAL が地下鉄テーブルで駆動するループ内の処理部分として動作します。地下鉄テーブルの個々のレコードは LATERAL サブクエリにどんどん送られるので、地下鉄レコードごとの最近傍の結果を得ます。

_images/knn4.png

EXPLAIN は地下鉄駅のループと、求めるループ内のインデックスによる ORDER BY を表示します:

                           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)