Jika Anda menambahkan bidang pembantu ke tabel koordinat, Anda dapat meningkatkan waktu respons kueri.
Seperti ini:
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`)
)
Jika Anda menggunakan TokuDB, Anda akan mendapatkan kinerja yang lebih baik jika Anda menambahkan indeks pengelompokan pada salah satu predikat, misalnya, seperti ini:
alter table Coordinates add clustering index c_lat(lat);
alter table Coordinates add clustering index c_lon(lon);
Anda membutuhkan lat dasar dan lon dalam derajat dan juga dosa (lat) dalam radian, cos (lat) * cos (lon) dalam radian dan cos (lat) * sin (lon) dalam radian untuk setiap titik. Kemudian Anda membuat fungsi mysql, seperti ini:
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
Ini memberi Anda jarak.
Jangan lupa untuk menambahkan indeks pada lat / lon sehingga tinju pembatas dapat membantu pencarian alih-alih memperlambatnya (indeks sudah ditambahkan dalam kueri CREATE TABLE di atas).
INDEX `lat_lon_idx` (`lat`, `lon`)
Diberikan tabel lama dengan hanya koordinat lat / lon, Anda dapat mengatur skrip untuk memperbaruinya seperti ini: (php menggunakan 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']
));
}
Kemudian Anda mengoptimalkan kueri yang sebenarnya untuk hanya melakukan perhitungan jarak ketika benar-benar diperlukan, misalnya dengan membatasi lingkaran (baik, oval) dari dalam dan luar. Untuk itu, Anda harus menghitung ulang beberapa metrik untuk kueri itu sendiri:
// 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));
Mengingat persiapan itu, kueri berlangsung seperti ini (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);
JELASKAN pada kueri di atas mungkin mengatakan bahwa itu tidak menggunakan indeks kecuali ada cukup hasil untuk memicu seperti itu. Indeks akan digunakan ketika ada cukup data dalam tabel koordinat. Anda dapat menambahkan FORCE INDEX (lat_lon_idx) ke SELECT untuk membuatnya menggunakan indeks tanpa memperhatikan ukuran tabel, sehingga Anda dapat memverifikasi dengan EXPLAIN bahwa itu berfungsi dengan benar.
Dengan contoh kode di atas Anda harus memiliki implementasi pencarian objek yang berjalan dan dapat diukur berdasarkan jarak dengan kesalahan minimal.