2009-11-13 9 views
11

Abbiamo una tabella ristorante con dati lat-long per ogni riga.Query SQL per l'esecuzione della ricerca del raggio basata su Latitudine Longitudine

Abbiamo bisogno di scrivere una query che esegue una ricerca per trovare tutti i ristoranti all'interno del raggio fornito, ad es. 1 miglio, 5 miglia ecc

Abbiamo la seguente query per questo scopo:

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

La tabella ha circa 23k righe. La dimensione del set di risultati è a volte strana, ad es. per una ricerca di 5,4 miglia, restituisce 880 righe e per 5,5 miglia restituisce 21k righe.

Questa tabella contiene i dati del ristorante per nyc, quindi la distribuzione reale non è conforme al set di risultati.

Domanda: C'È QUALCHE COSA SBAGLIATO Con questa domanda?

DB: MySQL, Longitudine: decimale (10,6), Latitude: DECIMAL (10,6)

+0

Cosa c'è che non va? Per i principianti, si desidera utilizzare UTM anziché le coordinate Lat/Long perché hanno un margine di errore inferiore per l'appiattimento ... –

+0

Controlla la risposta a [questa domanda] (http://stackoverflow.com/questions/574691/mysql-grande-cerchio-distance-haversine-formula). Problema simile – Ramin

risposta

3

Si consiglia di creare un indice SPATIAL sulla vostra tavola per rendere le ricerche più veloci.

Per fare questo, aggiungere una colonna POINT al vostro tavolo:

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 

consente di memorizzare il coords come UTM coordinate all'interno di una singola zona.

13

ESISTE QUALCHE COSA SBAGLIATA Con questa query ?

A mio parere la clausola WHERE sta per essere lento a causa della la matematica coinvolti, e l'uso delle funzioni nella clausola WHERE impedirà il database utilizzando un indice per velocizzare la query - così, in effetti, è esaminerà ogni ristorante nel database ed eseguirà la matematica a grandi cerchi su ogni riga, ogni volta che effettui una query.

Personalmente vorrei calcolare le coordinate TopLeft e BottomRight di un quadrato (che deve essere calcolato solo in modo grossolano usando pitagora) con lati uguali all'intervallo che si sta cercando, quindi eseguire il più complicato test della clausola WHERE su il sottoinsieme più piccolo di record che si trova all'interno di quel quadrato Lat/Long.

Con un indice su Lat & lungo nel database query

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

dovrebbe essere molto efficiente

(Si prega di notare che non ho alcuna conoscenza di MySQL in particolare, solo di MS SQL)

+1

Vedere anche: http://janmatuschek.de/LatitudeLongitudeBoundingCoordinates – Pete

0

Se i dati sono nel database di SQL Server, è possibile utilizzare questo:

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

Utilizzare una funzione, ad es. quello I posted here.

Quindi, chiedi ai ristoranti, ad es.per ottenere tutto entro un raggio di 5 miglia

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

Questo funziona bene con i dati del codice ZIP.