2009-11-13 6 views
11

各行にlat-longデータを持つレストランテーブルがあります。緯度経度に基づいて半径検索を実行するためのSQLクエリ

指定された半径内のすべてのレストランを検索する検索を実行するクエリを作成する必要があります。等の1マイル、5マイル

私たちは、この目的のために次のクエリを持っている:

***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行戻します。

この表にはnycのレストランデータが含まれているため、実際の配布は結果セットごとに行われません。

質問:このクエリでは何も間違っていますか?

DB:MySQLの、経度:DECIMAL(10,6)、緯度:DECIMAL(10,6)

+0

何が問題なのですか。初心者にとっては、フラット化のために誤差のマージンが小さいので、緯度/経度座標ではなくUTMを使用したいと考えています。 –

+0

[この質問に対する回答を確認してください](http://stackoverflow.com/questions/574691)/mysql-great-circle-distance-haversine-formula)を使用します。同様の問題。 – Ramin

答えて

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として座標を格納する必要があります。

13

これは何も間違っていますか?この では、

私の意見では、WHERE句は数学のために遅くなります。WHERE句で関数を使用すると、インデックスを使用してクエリを高速化することができなくなります。クエリを実行するたびに、データベース内のすべてのレストランを調べ、すべての行で大円の数学を実行します。

個人的には、あなたが探している範囲に等しい辺を持つ正方形(ピタゴラスを使用して粗雑に計算する必要がある)のTopLeftとBottomRightの座標を計算し、次により複雑なWHERE句のテストを実行しますその緯度/経線の範囲内にあるレコードのより小さなサブセット。データベース内の緯度ロング&上のインデックス

 
WHERE  MyLat >= @MinLat AND MyLat <= @MaxLat 
     AND MyLong >= @MinLong AND MyLong <= @MaxLong 

は非常に効率的であるべきクエリで

(私はMS SQLのは、具体的にMySQLのの知識がないことに注意してください)

+1

参照:http://janmatuschek.de/LatitudeLongitudeBoundingCoordinates – Pete

0

データがSQLサーバーデータベースにある場合は、これを使用できます。

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 
0

などの関数を使用します。 1つはI posted hereです。

次に、レストランに質問します。半径5マイル以内のすべてを取得する

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

これは、郵便番号のデータで問題なく動作します。

関連する問題