【実務・中級編】 空間演算関数 – PostgreSQL

位置情報データ、実は「沼」?PostGISで空間クエリを使いこなすための勘所

現場で地図データや位置情報を扱うことになったエンジニアの皆さん、こんにちは。

「住所から座標を出す」「特定のエリア内の店舗を探す」……一見単純そうですが、いざRDBで実装しようとすると、真っ先にぶち当たるのがPostGISの壁ですよね。標準のSQLだけじゃ太刀打ちできないし、かといってドキュメントを全部読み込むのも骨が折れる。

今日は、僕が実務で「これだけは押さえておけ!」と後輩に必ず伝える、PostGISの必須関数と、その裏にあるちょっとしたコツを解説します。これを押さえておけば、空間クエリで「重い・遅い・動かない」と頭を抱えることは激減するはずです。

—

1. まずは基本の「関係性」を問う:`ST_Intersects`

空間クエリで一番よく使うのが「ある地点が、特定の範囲に含まれているか?」という判定です。ここで迷わず使うべきなのが `ST_Intersects` です。

— 「この店舗(point)は、指定された配送エリア(polygon)の中にあるか?」
SELECT name
FROM stores
WHERE ST_Intersects(stores.location, delivery_areas.geom);

先輩からのアドバイス:
`ST_Contains` と迷う人が多いんだけど、基本は `ST_Intersects` でOK。なぜなら、これは「交差しているか」を判定する関数で、点だけでなく線や面同士の比較でも非常に汎用性が高いからです。迷ったらまずはこれ。

—

2. 「近さ」を測るなら:`ST_Distance`

「現在地から一番近いコンビニを探す」なんて要件、よくありますよね。

— 5km以内にある店舗を、近い順に並べる
SELECT name, ST_Distance(location, ST_MakePoint(139.7, 35.6)::geography) AS distance
FROM stores
WHERE ST_DWithin(location, ST_MakePoint(139.7, 35.6)::geography, 5000)
ORDER BY distance;

ここで重要なのが `ST_DWithin` です。`ST_Distance` を `WHERE` 句で直接使うと、全てのレコードに対して計算が走ってしまい、インデックスが効かず爆速で重くなります。「距離の判定」には必ず `ST_DWithin` を使う。 これ、テストに出るレベルで重要です。

—

3. エリアを広げる・くっつける:`ST_Buffer` と `ST_Union`

データ分析をやっていると、ポイントから一定範囲を作ったり、複数のエリアを結合したりしたくなります。

  • `ST_Buffer`: ポイントから「半径◯メートルの円」を作るのに使います。
  • `ST_Union`: 複数のポリゴンを一つにマージします。

— 店舗の周囲1kmを商圏エリアとして定義する
SELECT ST_Union(ST_Buffer(location::geography, 1000)::geometry)
FROM stores;

注意点: `ST_Buffer` は計算コストが非常に高いです。大量のデータに対して実行するとCPUが悲鳴を上げます。可能な限り「事前計算して結果をテーブルに持たせておく(マテリアライズドビューなど)」のが、僕らエンジニアの腕の見せ所です。

—

空間インデックス(GiST)を忘れるな!

ここまで関数を紹介してきましたが、どれだけ便利な関数も、インデックスがなければ宝の持ち腐れです。

CREATE INDEX idx_stores_location ON stores USING GIST (location);

PostgreSQLの標準インデックス(B-tree)ではなく、GiST(Generalized Search Tree) を使うのがポイント。これがないと、空間クエリは全件フルスキャンになってしまい、数万件のデータでもレスポンスが数秒単位で遅れることになります。「なんかクエリが遅いな?」と思ったら、まず `EXPLAIN ANALYZE` してインデックスが使われているか確認してください。

—

現場からのまとめ:最後は「型」に気をつけろ

PostGISで一番ハマるのが、実は座標系(SRID)と型の不一致です。
`geometry`型(平面)と `geography`型(地球儀)を混ぜて計算しようとすると、エラーになったり、距離の計算が微妙にズレたりします。

  • `geometry`: 地図を描画するような、平面的な処理に強い。
  • `geography`: 地球の丸みを考慮した、メートル単位の正確な距離計算に強い。

最初は混乱するかもしれませんが、「距離を測るなら `geography`」「形をいじるなら `geometry`」と使い分けるのが今のベストプラクティスです。

空間データは、一度コツを掴むとパズルのようで本当に面白いですよ。最初は難しく感じるかもしれませんが、まずは小さなクエリから試してみてください。何か詰まったら、またいつでも聞きに来てくださいね。

皆さんの開発が、少しでも快適になりますように!

コメント

タイトルとURLをコピーしました