2009-11-13 17 views
11

Tenemos una tabla de restaurante que tiene datos de latitud larga para cada fila.Consulta SQL para realizar una búsqueda de radio basada en la longitud de la latitud

Tenemos que escribir una consulta que realice una búsqueda para encontrar todos los restaurantes dentro del radio provisto, p. 1 milla, 5 millas, etc.

Tenemos la siguiente consulta para este propósito:

***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 tabla tiene unos 23k filas. El tamaño del conjunto de resultados es extraño algunas veces, p. para una búsqueda de 5.4 millas, devuelve 880 filas y para 5.5 millas, devuelve 21k filas.

Esta tabla contiene datos de restaurante para nyc, por lo que la distribución real no coincide con el conjunto de resultados.

Pregunta: HAY ALGO INCORRECTO Con esta consulta?

DB: MySQL, Longitud: DECIMAL (10,6), Latitud: DECIMAL (10,6)

+0

¿Qué pasa? Para empezar, querrá usar UTM en lugar de coordenadas Lat/Long porque tienen un margen de error más pequeño para aplanar ... –

+0

Consulte la respuesta a [esta pregunta] (http://stackoverflow.com/questions/574691/mysql-great-circle-distance-haversine-formula). Problema similar – Ramin

Respuesta

3

Es posible que desee crear un índice SPATIAL en su mesa para hacer las búsquedas más rápidas.

Para ello, agregue una columna POINT a su mesa:

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 

Debe almacenar coords como UTM coordenadas dentro de una sola zona.

13

HAY ALGO INCORRECTO Con esta consulta ?

En mi opinión la cláusula WHERE va a ser lento debido a las matemáticas involucradas, y el uso de funciones en la cláusula WHERE evitará que la base de datos mediante un índice para acelerar la consulta - por lo que, en efecto, se examinará todos los restaurantes de la base de datos y realizará las matemáticas del gran círculo en cada fila, cada vez que realice una consulta.

Personalmente calcularía las coordenadas TopLeft y BottomRight de un cuadrado (que solo debe calcularse crudamente usando pythagoras) con lados iguales al rango que está buscando, y luego realizaré la prueba de cláusula WHERE más complicada en el subconjunto más pequeño de registros que están dentro de ese cuadrado Lat/Long.

Con un índice en Lat & largo de la base de datos de la consulta

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

debe ser muy eficiente

(Tenga en cuenta que no tengo conocimiento de MySQL en concreto, sólo en MS SQL)

+1

Ver también: http://janmatuschek.de/LatitudeLongitudeBoundingCoordinates – Pete

0

Si los datos están en la base de datos del servidor SQL, puede utilizar esto:

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

Use una función, p. el uno I posted here.

Luego, consulte sus restaurantes, p.para obtener todo dentro de un radio de 5 millas

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

Esto funciona bien con los datos del código postal.

Cuestiones relacionadas