Nếu bạn thêm trường trợ giúp vào bảng tọa độ, bạn có thể cải thiện thời gian phản hồi của truy vấn.
Như thế này:
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`)
)
Nếu bạn đang sử dụng TokuDB, bạn sẽ có được hiệu suất tốt hơn nếu bạn thêm phân nhóm chỉ số trên một trong các vị từ, ví dụ, như thế này:
alter table Coordinates add clustering index c_lat(lat);
alter table Coordinates add clustering index c_lon(lon);
Bạn sẽ cần lat và lon cơ bản về độ cũng như sin (lat) theo radian, cos (lat) * cos (lon) theo radian và cos (lat) * sin (lon) theo radian cho mỗi điểm . Sau đó, bạn tạo một hàm mysql, SMTH như thế này:
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
này cung cấp cho bạn khoảng cách.
Đừng quên thêm chỉ mục vào lat/lon để boxing giới hạn có thể giúp tìm kiếm thay vì làm chậm nó xuống (chỉ mục đã được thêm vào truy vấn TẠO TẠO ở trên).
INDEX `lat_lon_idx` (`lat`, `lon`)
Với một bảng cũ chỉ với tọa độ/lon lat, bạn có thể thiết lập một kịch bản để cập nhật nó như thế này: (php sử dụng 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']
));
}
Sau đó bạn tối ưu hóa truy vấn thực tế để chỉ thực hiện phép tính khoảng cách khi thực sự cần thiết, ví dụ bằng cách bao quanh hình tròn (tốt, hình bầu dục) từ trong và ngoài. Cho rằng, bạn sẽ cần phải precalculate vài số liệu cho truy vấn bản thân:
// 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));
Với những chuẩn bị, truy vấn đi một cái gì đó như thế này (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);
GIẢI THÍCH trên các truy vấn trên có thể nói rằng nó không sử dụng chỉ mục trừ khi có đủ kết quả để kích hoạt như vậy. Chỉ mục sẽ được sử dụng khi có đủ dữ liệu trong bảng tọa độ. Bạn có thể thêm FORCE INDEX (lat_lon_idx) vào lệnh SELECT để sử dụng chỉ mục không liên quan đến kích thước bảng, vì vậy bạn có thể xác minh bằng GIẢI THÍCH rằng nó hoạt động chính xác.
Với các mẫu mã ở trên, bạn nên thực hiện tìm kiếm đối tượng đang hoạt động và có thể mở rộng theo khoảng cách với lỗi tối thiểu.
Dựa trên tất cả các câu trả lời tuyệt vời dưới đây, [ở đây đang làm việc mẫu của công thức Haversine trong hành động] (http://sqlfiddle.com/#!2/abba1/2/0) –
Cảm ơn bạn đã chia sẻ Michael.M –
http://stackoverflow.com/a/40272394/1281385 Có một ví dụ về cách đảm bảo chỉ mục được nhấn – exussum