14

我们有一个restaurant表,每行都有经纬度数据。

我们需要编写一个查询来执行搜索以查找提供的半径内的所有餐馆,例如 1 英里、5 英里等。

query为此,我们有以下内容:

***Parameters***

Longitude: -74.008680
Latitude: 40.711676
Radius: 1 mile

***Query***

SELECT *
FROM restaurant
WHERE (
POW( ( 69.1 * ( Longitude - -74.008680 ) * cos( 40.711676 / 57.3 ) ) , 2 ) + POW( ( 69.1 * ( Latitude - 40.711676 ) ) , 2 )
) < ( 1 *1 );

该表有大约 23k 行。结果集的大小有时很奇怪,例如对于 5.4 英里的搜索,它返回 880 行,而对于 5.5 英里,它返回 21k 行。

此表包含纽约市的餐厅数据 - 因此实际分布与结果集不同。

问题:这个查询有什么问题吗?

DB:MySQL,经度:DECIMAL(10,6),纬度:DECIMAL(10,6)

4

4 回答 4

16

这个查询有什么问题吗?

在我看来,由于涉及数学,WHERE 子句会很慢,并且在 WHERE 子句中使用函数会阻止数据库使用索引来加速查询 - 因此,实际上,您将检查数据库,并在每次查询时对每一行执行大圆数学运算。

就我个人而言,我会计算一个正方形的 TopLeft 和 BottomRight 坐标(只需要使用毕达哥拉斯进行粗略计算),其边等于您正在寻找的范围,然后对较小的子集执行更复杂的 WHERE 子句测试该纬度/经度方块内的记录。

通过数据库中的经纬度索引,查询

其中 MyLat >= @MinLat AND MyLat <= @MaxLat
      AND MyLong >= @MinLong AND MyLong <= @MaxLong

应该非常高效

(请注意,我对 MySQL 没有特别的了解,只有 MS SQL)

于 2009-11-13T16:43:33.823 回答
3

您可能希望在表上创建SPATIAL索引以加快搜索速度。

为此,POINT请在表中添加一列:

ALTER TABLE restaurant ADD coords POINT NOT NULL;

CREATE SPATIAL INDEX sx_restaurant_coords ON restaurant (coords);

SELECT  *
FROM    restaurant
WHERE   MBRContains(coords, LineString(Point(583734 - 1609, 4507223 - 1609), Point(583734 + 1609, 4507223 + 1609))
        AND GLength(LineString(Point(583734, 4507223), coords)) <= 1609

您应该将坐标存储coordsUTM单个区域内。

于 2009-11-13T16:23:26.523 回答
0

如果您的数据在 SQL Server 数据库中,您可以使用:

CREATE PROC up_FindZipCodesWithinRadius

    @ZipCode char(5) ,
    @GivenMileRadius int
AS
SET NOCOUNT ON

DECLARE @lat1 float, 
    @long1 float

SELECT  @lat1= latitude,
    @long1 = longitude 
FROM ZipSource
WHERE zipcode = @ZipCode

SELECT ZipCode ,DistanceInMiles
FROM
(
    SELECT  ZipCode,3958.75 * ( Atan(Sqrt(1 - power(((Sin(@Lat1/57.2958) * Sin(latitude/57.2958)) + 
            (Cos(@Lat1/57.2958) * Cos(latitude/57.2958) * Cos((longitude/57.2958) - (@Long1/57.2958)))), 2)) / 
            ((Sin(@Lat1/57.2958) * Sin(latitude/57.2958)) + (Cos(@Lat1/57.2958) * Cos(latitude/57.2958) * 
            Cos((longitude/57.2958) - (@Long1/57.2958)))))) as DistanceInMiles
FROM ZipSource
) a
WHERE a.DistanceInMiles <= @GivenMileRadius
--AND ZipCode <> @ZipCode
ORDER BY DistanceInMiles

GO

EXEC up_FindZipCodesWithinRadius '35085',20
GO

DROP PROC up_FindZipCodesWithinRadius
于 2009-11-20T03:58:16.030 回答
0

使用一个功能,例如我在这里发布的那个。

然后,查询您的餐厅,例如获取 5 英里半径范围内的所有内容

select * from restaurants 
  where dbo.udf_Haversine(latitude, longitude, @lat, @long) < 5

这对邮政编码数据执行良好。

于 2009-12-08T04:39:48.310 回答