У вас есть неплохая ссылка для поиска расстояния mySQL.
Забудьте об Oracle Spatial. Слишком много кода, слишком много сложности, недостаточно добавленной стоимости.
Вот запрос, который поможет. Это использует расстояния в уставных милях. РЕДАКТИРОВАТЬ Это исправляет ошибку, упомянутую mdarwin, за счет проверки разделения, если вы пытаетесь использовать ее для местоположения на северном или южном полюсе.
SELECT id, city, LATITUDE, LONGITUDE, distance
FROM
(
SELECT id,
city,
LATITUDE, LONGITUDE,
(3959 * ACOS(COS(RADIANS(LATITUDE))
* COS(RADIANS(mylat))
* COS(RADIANS(LONGITUDE) - RADIANS(mylng))
+ SIN(RADIANS(LATITUDE))
* SIN(RADIANS(mylat))
))
AS distance,
b.mydst
FROM Cities
JOIN (
SELECT :LAT AS mylat,
:LONG AS mylng,
:RADIUS_LIMIT AS mydst
FROM DUAL
)b ON (1 = 1)
WHERE LATITUDE >= mylat -(mydst/69)
AND LATITUDE <= mylat +(mydst/69)
AND LONGITUDE >= mylng -(mydst/(69 * COS(RADIANS(mylat))))
AND LONGITUDE <= mylng +(mydst/(69 * COS(RADIANS(mylat))))
)a
WHERE distance <= mydst
ORDER BY distance
Если вы работаете в километрах, измените mydst / 69 на mydst / 111.045 и измените 3959 на 6371.4. (1/69 преобразует мили в градусы; 3959 - это значение радиуса планеты.)
Теперь вы, вероятно, захотите использовать этот большой запрос в качестве "волшебного черного ящика". Не делай этого! Это не очень сложно понять, и если вы поймете это, вы сможете сделать лучше. Вот что происходит.
Этот пункт является сердцем того, что делает запрос быстрым. Он ищет в вашей таблице городов близлежащие города до указанной вами точки.
WHERE LATITUDE >= mylat -(mydst/69)
AND LATITUDE <= mylat +(mydst/69)
AND LONGITUDE >= mylng -(mydst/(69 * COS(RADIANS(mylat))))
AND LONGITUDE <= mylng +(mydst/(69 * COS(RADIANS(mylat))))
Чтобы это работало, вам определенно нужен индекс для вашего столбца LATITUDE. Индекс на вашем столбце LONGITUDE также немного поможет. Он выполняет приблизительный поиск, ища ряды, которые находятся в квази-прямоугольном пятне на поверхности земли рядом с вашей точкой. Он выбирает слишком много городов, но не слишком много.
Этот пункт позволяет вам исключить лишние города из набора результатов:
WHERE distance <= mydst
Этот пункт является формулой haversine, которая вычисляет расстояние по большому кругу между каждым городом и вашей точкой.
(3959 * ACOS(COS(RADIANS(LATITUDE))
* COS(RADIANS(mylat))
* COS(RADIANS(LONGITUDE) - RADIANS(mylng))
+ SIN(RADIANS(LATITUDE))
* SIN(RADIANS(mylat))
Этот пункт позволяет вам ввести вашу точку и ваш радиус-предел только один раз как связанные переменные для вашего запроса. Это полезно, потому что различные формулы используют эти переменные несколько раз.
SELECT :LAT AS mylat,
:LONG AS mylng,
:RADIUS_LIMIT AS mydst
FROM DUAL
Остальная часть запроса просто упорядочивает вещи, поэтому вы выбираете и упорядочиваете по расстоянию.
Вот более полное объяснение: http://www.plumislandmedia.net/mysql/haversine-mysql-nearest-loc/