web-dev-qa-db-ja.com

距離順

近くのカフェを返すクエリがある場合:

SELECT * FROM cafes c WHERE (
   ST_DWithin(
   ST_GeographyFromText(
     'SRID=4326;POINT(' || c.longitude || ' ' || c.latitude || ')'
   ),
   ST_GeographyFromText('SRID=4326;POINT(-76.000000 39.000000)'),
     2000
   )
)

距離を選択して距離で並べ替えるにはどうすればよいですか?
これよりも効率的な方法はありますか:

 SELECT id, 
 ST_Distance(ST_GeographyFromText('SRID=4326;POINT(-76.000000 39.000000)'),
             ST_GeographyFromText(
             'SRID=4326;POINT(' || c.longitude || ' ' || c.latitude || ')')      
             ) as distance 
 FROM cafes c
   WHERE (
   ST_DWithin(
     ST_GeographyFromText(
     'SRID=4326;POINT(' || c.longitude || ' ' || c.latitude || ')'
   ),
    ST_GeographyFromText('SRID=4326;POINT(-76.000000 39.000000)'),
   2000
 )
 ) order by distance
9

最初、使用

_ST_SetSRID(ST_MakePoint(c.longitude, c.latitude),4326)::geography
_

の代わりに

ST_GeographyFromText('SRID=4326;POINT(' || c.longitude || ' ' || c.latitude || ')')

ドキュメントごと:

OGCに準拠していない_ST_MakePoint_は、一般に_ST_GeomFromText_および_ST_PointFromText_よりも高速で正確です。また、WKTではなく未加工の座標がある場合にも使用する方が簡単です。

次へ、クエリを短くし、検索パラメータを1回だけ入力する(パフォーマンスに大きな影響を与えない)には、を使用しますサブクエリ(またはCTE):

_SELECT id
     , ST_Distance(t.x
                 , ST_SetSRID(ST_MakePoint(c.longitude, c.latitude),4326)::geography) AS dist
FROM   cafes c
    , (SELECT ST_GeographyFromText('SRID=4326;POINT(-76.000000 39.000000)')) AS t(x)
WHERE  ST_DWithin(t.x
                , ST_SetSRID(ST_MakePoint(c.longitude, c.latitude),4326)::geography, 2000)
ORDER  BY dist;_

最後に、大きなテーブルでこれを高速にするには、Gistインデックスが必要です。 ST_DWithin()のドキュメントごと

この関数呼び出しには、ジオメトリで使用可能なインデックスを利用する境界ボックスの比較が自動的に含まれます。

答えの最初の式で function index を使用してこれを機能させることができます。しかし、最初にgeographyタイプの列を格納し(名前をthegeogとしましょう)、次のようなプレーンなGistインデックスを作成します。

_CREATE INDEX cafes_thegeog_Gist ON cafes USING Gist(thegeog);
_

このはるかに単純で高速なクエリに到達します。

_SELECT id, ST_Distance(t.x, thegeog) AS distance 
FROM   cafes c
    , (SELECT ST_GeographyFromText('SRID=4326;POINT(-76.000000 39.000000)')) AS t(x)
WHERE  ST_DWithin(t.x, thegeog, 2000)
ORDER  BY distance;
_

コメントで@ LR1234567によって指摘されているように、geographygeographyと一致するように更新しました。別の方法として、geometryを使用できます。ここで使用されるすべての関数は両方で機能します(_ST_MakePoint_を除き、追加されたキャストを除く)。 違いは何ですか?

n最も近いカフェをすべて半径内で取得したい場合は、 "nearest neighbour" search 。多くの場合より便利です。

11