2009-11-13 9 views
11

Nous avons une table de restaurant qui a des données lat-longues pour chaque ligne.Requête SQL pour la recherche de rayon basée sur Latitude Longitude

Nous devons écrire une requête qui effectue une recherche pour trouver tous les restaurants dans le rayon fourni, par ex. 1 mile, 5 miles etc.

Nous avons la requête suivante à cet effet:

***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); 

Le tableau a environ 23k lignes. La taille de l'ensemble de résultats est parfois étrange, par ex. pour une recherche de 5,4 milles, elle renvoie 880 lignes et pour 5,5 milles, elle renvoie 21k lignes.

Cette table contient les données de restaurant pour nyc - de sorte que la distribution réelle n'est pas conforme au jeu de résultats.

Question: Y A-T-IL UNE QUELCONQUE FAUTE? Avec cette requête?

DB: MySQL, Longitude: DECIMAL (10,6), Latitude: DECIMAL (10,6)

+0

Qu'est-ce qui ne va pas? Pour commencer, vous voulez être en utilisant UTM plutôt que Lat/coordonnées longues parce qu'ils ont une marge d'erreur plus pour aplatir ... –

+0

Vérifiez la réponse à [cette question] (http://stackoverflow.com/questions/574691/mysql-grand-cercle-distance-haversine-formule). Problème similaire. – Ramin

Répondre

3

Vous pouvez créer un index SPATIAL sur votre table pour accélérer les recherches.

Pour ce faire, ajoutez une colonne POINT à votre table:

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 

Vous devez stocker coords comme UTM coordonnées au sein d'une seule zone.

13

Y AT-IL QUELQUE CHOSE FAUT Avec cette requête ?

À mon avis, la clause WHERE va être lente en raison des mathématiques impliqués, et l'utilisation des fonctions dans la clause WHERE empêchera la base de données en utilisant un index pour accélérer la requête - donc, en effet, vous examinera chaque restaurant dans la base de données, et effectuera les maths du grand cercle sur chaque ligne, chaque fois que vous faites une requête. Personnellement, je calcule les coordonnées TopLeft et BottomRight d'un carré (qui doit seulement être crustly calculé en utilisant pythagoras) avec des côtés égaux à la gamme que vous recherchez, puis effectuez le test de la clause WHERE plus compliqué sur le plus petit sous-ensemble d'enregistrements qui se trouvent dans ce carré Lat/Long.

Avec un index sur Lat & longue dans la base de données la requête

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

devrait être très efficace

(S'il vous plaît noter que je ne connais pas de MySQL spécifiquement, que de MS SQL)

+1

Voir aussi: http://janmatuschek.de/LatitudeLongitudeBoundingCoordinates – Pete

0

Si vos données sont dans la base de données du serveur SQL, vous pouvez utiliser ceci:

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

Utilisez une fonction, par ex. celui I posted here.

Ensuite, interrogez vos restaurants, par ex.pour obtenir tout dans un rayon de 5 mile

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

Cela fonctionne bien avec les données de code postal.

Questions connexes