按距离排序

Gan*_*row 11 postgresql postgis postgresql-9.2 gist-index

如果我有一个关于返回附近咖啡馆的查询:

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
   )
)
Run Code Online (Sandbox Code Playgroud)

如何选择距离以及按距离排序?
有没有比这更有效的方法:

 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
Run Code Online (Sandbox Code Playgroud)

Erw*_*ter 12

首先,使用

ST_SetSRID(ST_MakePoint(c.longitude, c.latitude),4326)::geography
Run Code Online (Sandbox Code Playgroud)

代替

ST_GeographyFromText('SRID=4326;POINT(' || c.longitude || ' ' || c.latitude || ')')
Run Code Online (Sandbox Code Playgroud)

根据文档:

ST_MakePoint虽然不符合 OGC 标准,但通常比ST_GeomFromText和更快、更精确ST_PointFromText。如果您有原始坐标而不是 WKT,它也更容易使用。

接下来,要使查询更短并且只输入一次搜索参数(对性能没有太大影响),请使用子查询(或 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;
Run Code Online (Sandbox Code Playgroud)

最后,您需要一个GiST 索引来加快大表的速度。 每个文档ST_DWithin()

此函数调用将自动包含一个边界框比较,它将利用几何上可用的任何索引。

您可以将其与答案开头的表达式的功能索引一起使用。但是我会存储一个geography类型列以开始(让我们命名它thegeog)并创建一个简单的 GiST 索引,如:

CREATE INDEX cafes_thegeog_gist ON cafes USING gist(thegeog);
Run Code Online (Sandbox Code Playgroud)

到达这个更简单和更快的查询:

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;
Run Code Online (Sandbox Code Playgroud)

更新以匹配geographygeography,如通过评论@ LR1234567指出。作为替代方案,您可以使用geometry. 这里使用的所有函数都适用于两者(除了ST_MakePoint,因此附加了强制转换)。有什么不同?

如果您想获得半径范围内的n 个最近的咖啡馆,请考虑“最近的邻居”搜索。往往更方便。

  • 您不应该将几何与地理混为一谈。您应该这样做: ST_SetSRID(ST_MakePoint(c.longitude, c.latitude),4326)::geography - 请注意 ST_MakePoint 返回几何。 (2认同)