ORDER BY ABS(someLatitude - latitude) ASC,
ABS(someLongitude - longitude) ASC
WTF? Это соберет вместе все города с очень похожими широтами - даже те, что находятся на другом конце земного шара!
mysql> SELECT country, city, lat, lng FROM `cities`
WHERE population > 0
ORDER BY ABS(lat - 36) ASC,
ABS(lng - 0) ASC LIMIT 10;
+---------+-----------+---------+----------+
| country | city | lat | lng |
+---------+-----------+---------+----------+
| jp | Okegawa | 36 | 139.557 |
| us | Sapulpa | 35.9986 | -96.1139 |
| us | Avenal | 36.0042 | -120.128 |
| cn | Zhucheng | 35.9947 | 119.397 |
| us | Durham | 35.9939 | -78.8989 |
| us | Espanola | 35.9911 | -106.08 |
| jp | Chichibu | 35.9903 | 139.076 |
| us | Oak Ridge | 36.0103 | -84.2697 |
| ir | Baneh | 35.9894 | 45.8953 |
| es | Tarifa | 36.0125 | -5.60556 |
+---------+-----------+---------+----------+
10 rows in set (0.90 sec)
Обратите внимание, что города в Японии, США, Китае, Ирландии и Испании "близки" друг к другу на основании этого ORDER BY
. (В моем списке 3M городов, многие с населением = 0.)
Первая оптимизация - выбрать максимальное расстояние и нарисовать «ограничивающую рамку» вокруг центра. И имеют индекс:
mysql> SELECT country, city, lat, lng FROM `cities` WHERE population > 0
-> AND lat BETWEEN 36-2 AND 36+2
-> AND lng BETWEEN -84-3 AND -84+3
-> LIMIT 10;
+---------+---------------+---------+----------+
| country | city | lat | lng |
+---------+---------------+---------+----------+
| us | Gadsden | 34.0142 | -86.0067 |
| us | Cedartown | 34.0536 | -85.255 |
| us | Acworth | 34.0658 | -84.6769 |
| us | Kennesaw | 34.0233 | -84.6156 |
| us | Woodstock | 34.1014 | -84.5194 |
| us | Mountain Park | 34.0808 | -84.4114 |
| us | Roswell | 34.0231 | -84.3617 |
| us | Alpharetta | 34.0753 | -84.2942 |
| us | Duluth | 34.0028 | -84.1447 |
| us | Suwanee | 34.0514 | -84.0714 |
+---------+---------------+---------+----------+
с INDEX(lat)
или INDEX(lng)
или INDEX(lat,lng)
или INDEX(lng,lat)
. (Эти индексы примерно одинаково хороши / плохи. Но все же нужно было посмотреть на 257 тыс. Строк, а именно на все строки в этой 4-градусной (276-мильной или 444-километровой) полосе.
Если вы не ограничите расстояние, индекс будет бесполезен, поскольку он будет смотреть на все строки.
Для вычисления расстояния (либо пифагорейского, либо большого круга) для получения «ближайшего» потребуется какое-то условие ORDER BY
.
Чтобы стать действительно эффективным, нужно гораздо больше усилий: http://mysql.rjweb.org/doc.php/latlng
В нем подробно обсуждается, почему проблема является сложной.