距離にどの式を使用するかはそれほど重要ではありません。さらに重要なのは、読み取り、処理、およびソートする必要のある行の数です。最良の場合、WHERE句の条件のインデックスを使用して、処理される行の数を制限できます。あなたはあなたの場所を分類することを試みることができます-しかしそれがうまくいくかどうかはあなたのデータの性質に依存します。また、使用する「カテゴリ」を見つける必要があります。より一般的な解決策は、 SPATIAL INDEXを使用することです。 およびST_Within() 機能。
それでは、いくつかのテストを実行してみましょう。
私のDB(MySQL 5.7.18)には、次の表があります。
CREATE TABLE `cities` (
`cityId` MEDIUMINT(9) UNSIGNED NOT NULL AUTO_INCREMENT,
`country` CHAR(2) NOT NULL COLLATE 'utf8mb4_unicode_ci',
`city` VARCHAR(100) NOT NULL COLLATE 'utf8mb4_unicode_ci',
`accentCity` VARCHAR(100) NOT NULL COLLATE 'utf8mb4_unicode_ci',
`region` CHAR(2) NULL DEFAULT NULL COLLATE 'utf8mb4_unicode_ci',
`population` INT(10) UNSIGNED NULL DEFAULT NULL,
`latitude` DECIMAL(10,7) NOT NULL,
`longitude` DECIMAL(10,7) NOT NULL,
`geoPoint` POINT NOT NULL,
PRIMARY KEY (`cityId`),
SPATIAL INDEX `geoPoint` (`geoPoint`)
) COLLATE='utf8mb4_unicode_ci' ENGINE=InnoDB
データは、無料の世界都市データベース から取得されます。 3173958(3.1M)行が含まれています。
geoPoint
に注意してください 冗長であり、POINT(longitude, latitude)
と同じです。 。
ユーザーがロンドンのどこかにいることを考慮してください
set @lon = 0.0;
set @lat = 51.5;
cities
から最寄りの場所を見つけたい テーブル。
「些細な」クエリは
select c.cityId, c.accentCity, st_distance_sphere(c.geoPoint, point(@lon, @lat)) as dist
from cities c
order by dist
limit 1
結果は
です988204 Blackwall 1085.8212159861014
実行時間:〜4.970秒
それほど複雑でない関数ST_Distance()
を使用する場合 、実行時間は約4.580秒で同じ結果が得られますが、それほど違いはありません。
テーブルにジオポイントを保存する必要はないことに注意してください。 (point(c.longitude, c.latitude)
をうまく利用できます c.geoPoint
の代わりに 。驚いたことに、それはさらに高速です(ST_Distance
の場合は約3.6秒) ST_Distance_Sphere
の場合は約4.0秒 )。 geoPoint
がなかった場合は、さらに高速になる可能性があります まったく列。しかし、それでもそれほど重要ではありません。ユーザーを待たせたくないので、もっとうまくできる場合は、休憩のためにログに記録してください。
次に、 SPATIAL INDEXの使用方法を見てみましょう。 ST_Within()
を使用 。
ポリゴンを定義する必要があります 最も近い場所が含まれます。簡単な方法は、 ST_Buffer()を使用することです。 これにより、32ポイントのポリゴンが生成され、ほぼ円になります*。
set @point = point(@lon, @lat);
set @radius = 0.1;
set @polygon = ST_Buffer(@point, @radius);
select c.cityId, c.accentCity, st_distance_sphere(c.geoPoint, point(@lon, @lat)) as dist
from cities c
where st_within(c.geoPoint, @polygon)
order by dist
limit 1
結果は同じです。実行時間は約0.000秒です(これが私のクライアント( HeidiSQL )言う)
* @radius
に注意してください は度で表記されるため、ポリゴンは円ではなく楕円のようになります。しかし、私のテストでは、単純で遅いソリューションの場合と常に同じ結果が得られました。ただし、本番コードで使用する前に、さらに多くのエッジケースを調査します。
次に、アプリケーション/データに最適な半径を見つける必要があります。小さすぎると、結果が得られなかったり、最も近いポイントを見逃したりする可能性があります。大きすぎる場合は、処理する行が多すぎる可能性があります。
ここに、与えられたテストケースのいくつかの数字があります:
- @radius =0.001:結果なし
- @radius =0.01:正確に1つの場所(ラッキーのようなもの)-実行時間〜0.000秒
- @radius =0.1:55の場所-実行時間〜0.000秒
- @radius =1.0:2183の場所-実行時間〜0.030秒