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 );
В таблице содержится около 23 тыс. строк. Размер набора результатов является странным по времени, например. для поиска в 5,4 мили он возвращает 880 строк и 5,5 миль, он возвращает строки в 21к.
В этой таблице содержатся данные ресторана для nyc - поэтому реальное распределение не соответствует набору результатов.
Вопрос: ТАК ЧТО НЕПРАВИЛЬНО С этим вопросом?
DB: MySQL, долгота: DECIMAL (10,6), Широта: DECIMAL (10,6)
Ответы
Ответ 1
ЭТО НИЧЕГО НЕПРАВИЛЬНО С этим запрос?
По моему мнению, предложение WHERE будет медленным из-за задействованной математики, а использование функций в предложении WHERE предотвратит использование базы данных с индексом для ускорения запроса - поэтому, в действительности, вы будете изучать каждый ресторан в базе данных и выполнять математику большого круга в каждой строке, каждый раз, когда вы делаете запрос.
Лично я бы вычислил координаты TopLeft и BottomRight квадрата (который нужно только грубо рассчитать с помощью pythagoras) со сторонами, равными диапазону, который вы ищете, а затем выполните более сложное условие WHERE clause на меньшее подмножество записей, которые находятся внутри этого квадрата Lat/Long.
С индексом Lat и Long в базе данных запрос
WHERE MyLat >= @MinLat AND MyLat <= @MaxLat
AND MyLong >= @MinLong AND MyLong <= @MaxLong
должен быть очень эффективным
(Обратите внимание, что я не знаю MySQL специально, только MS SQL)
Ответ 2
Вы можете создать индекс 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
Вы должны хранить coords
как UTM
координаты в пределах одной зоны.
Ответ 3
Если ваши данные находятся в базе данных 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
Ответ 4
Используйте функцию, например. один Я разместил здесь.
Затем попросите свои рестораны, например. получить все в радиусе 5 миль
select * from restaurants
where dbo.udf_Haversine(latitude, longitude, @lat, @long) < 5
Это отлично работает с данными ZIP-кода.