Якщо додати допоміжні поля до таблиці координат, ви можете покращити час відповіді на запит.
Подобається це:
CREATE TABLE `Coordinates` (
`id` INT(10) UNSIGNED NOT NULL COMMENT 'id for the object',
`type` TINYINT(4) UNSIGNED NOT NULL DEFAULT '0' COMMENT 'type',
`sin_lat` FLOAT NOT NULL COMMENT 'sin(lat) in radians',
`cos_cos` FLOAT NOT NULL COMMENT 'cos(lat)*cos(lon) in radians',
`cos_sin` FLOAT NOT NULL COMMENT 'cos(lat)*sin(lon) in radians',
`lat` FLOAT NOT NULL COMMENT 'latitude in degrees',
`lon` FLOAT NOT NULL COMMENT 'longitude in degrees',
INDEX `lat_lon_idx` (`lat`, `lon`)
)
Якщо ви використовуєте TokuDB, ви отримаєте ще кращу ефективність, якщо додати індекси кластеризації за будь-яким із предикатів, наприклад, таким:
alter table Coordinates add clustering index c_lat(lat);
alter table Coordinates add clustering index c_lon(lon);
Вам знадобляться основні lat і lon в градусах, а також sin (lat) в радіанах, cos (lat) * cos (lon) в радіанах і cos (lat) * sin (lon) в радіанах для кожної точки. Тоді ви створюєте функцію mysql, smth так:
CREATE FUNCTION `geodistance`(`sin_lat1` FLOAT,
`cos_cos1` FLOAT, `cos_sin1` FLOAT,
`sin_lat2` FLOAT,
`cos_cos2` FLOAT, `cos_sin2` FLOAT)
RETURNS float
LANGUAGE SQL
DETERMINISTIC
CONTAINS SQL
SQL SECURITY INVOKER
BEGIN
RETURN acos(sin_lat1*sin_lat2 + cos_cos1*cos_cos2 + cos_sin1*cos_sin2);
END
Це дає відстань.
Не забудьте додати індекс на lat / lon, щоб обмежувальний бокс міг допомогти пошуку замість уповільнення його (індекс уже додано у запиті CREATE TABLE вище).
INDEX `lat_lon_idx` (`lat`, `lon`)
Враховуючи стару таблицю з лише координатами lat / lon, ви можете налаштувати сценарій для її оновлення так: (php за допомогою meekrodb)
$users = DB::query('SELECT id,lat,lon FROM Old_Coordinates');
foreach ($users as $user)
{
$lat_rad = deg2rad($user['lat']);
$lon_rad = deg2rad($user['lon']);
DB::replace('Coordinates', array(
'object_id' => $user['id'],
'object_type' => 0,
'sin_lat' => sin($lat_rad),
'cos_cos' => cos($lat_rad)*cos($lon_rad),
'cos_sin' => cos($lat_rad)*sin($lon_rad),
'lat' => $user['lat'],
'lon' => $user['lon']
));
}
Потім ви оптимізуєте фактичний запит, щоб робити розрахунок відстані лише тоді, коли це дійсно потрібно, наприклад, обмежуючи коло (ну овал) зсередини та зовні. Для цього вам потрібно буде перерахувати кілька показників для самого запиту:
// assuming the search center coordinates are $lat and $lon in degrees
// and radius in km is given in $distance
$lat_rad = deg2rad($lat);
$lon_rad = deg2rad($lon);
$R = 6371; // earth's radius, km
$distance_rad = $distance/$R;
$distance_rad_plus = $distance_rad * 1.06; // ovality error for outer bounding box
$dist_deg_lat = rad2deg($distance_rad_plus); //outer bounding box
$dist_deg_lon = rad2deg($distance_rad_plus/cos(deg2rad($lat)));
$dist_deg_lat_small = rad2deg($distance_rad/sqrt(2)); //inner bounding box
$dist_deg_lon_small = rad2deg($distance_rad/cos(deg2rad($lat))/sqrt(2));
Враховуючи ці препарати, запит має щось подібне (php):
$neighbors = DB::query("SELECT id, type, lat, lon,
geodistance(sin_lat,cos_cos,cos_sin,%d,%d,%d) as distance
FROM Coordinates WHERE
lat BETWEEN %d AND %d AND lon BETWEEN %d AND %d
HAVING (lat BETWEEN %d AND %d AND lon BETWEEN %d AND %d) OR distance <= %d",
// center radian values: sin_lat, cos_cos, cos_sin
sin($lat_rad),cos($lat_rad)*cos($lon_rad),cos($lat_rad)*sin($lon_rad),
// min_lat, max_lat, min_lon, max_lon for the outside box
$lat-$dist_deg_lat,$lat+$dist_deg_lat,
$lon-$dist_deg_lon,$lon+$dist_deg_lon,
// min_lat, max_lat, min_lon, max_lon for the inside box
$lat-$dist_deg_lat_small,$lat+$dist_deg_lat_small,
$lon-$dist_deg_lon_small,$lon+$dist_deg_lon_small,
// distance in radians
$distance_rad);
ПОЯСНЕННЯ на вищезазначеному запиті може сказати, що він не використовує індекс, якщо не вистачить результатів для його запуску. Індекс буде використаний, коли в таблиці координат буде достатньо даних. Ви можете додати FORCE INDEX (lat_lon_idx) до SELECT, щоб змусити його використовувати індекс, не залежно від розміру таблиці, так що ви можете перевірити EXPLAIN, що він працює правильно.
З наведеними вище зразками коду ви повинні мати працюючу та масштабовану реалізацію пошуку об’єктів на відстані з мінімальною помилкою.