1

現在、私は100k +行のテーブルをいくつか持っています。私は次のようなデータを検索しようとしています。半径の範囲内のポイントのために大きなテーブルを検索する最も速い方法は何ですか?MySQL(緯度経度)

SELECT 
*, SQRT(POW(69.1 * (latitude - '49.1044302'), 2) + POW(69.1 * ('-122.801094' - longitude) * COS(latitude/57.3), 2)) AS distance 
FROM stops 
HAVING distance < 5 
ORDER BY distance limit 100 

現在のところ、この方法は高負荷では速度が遅くなります。いくつかのクエリは完了までに20秒以上かかります。

これを最適化する方法がわかっていれば、すばらしいことになります。

+0

[_scalable lat/lng searches_](https://mariadb.com/kb/en/latitudelongitude-indexing/) –

+0

提案されているようにmysqlの地理空間型をチェックアウトしましたか? – e4c5

答えて

2

まず、地理空間データがたくさんある場合は、このような計算ではなく、mysqlの地理空間拡張を使用する必要があります。 create spatial indexesこれは多くのクエリを高速化し、上記のような長い引き出しクエリを書く必要はありません。

ST_Distanceとの比較を使用するか、ST_withinと一緒に関心のある半径を持つジオメトリを作成すると、良い結果が得られ、現在よりもはるかに高速になる可能性があります。しかしこれを達成する最善の方法は、ST_Dwithinはまだmysqlに実装されていません。

+1

空間索引を追加し、空間関数を使用して大まかな一致*を取得し、空間分析関数と共にWHERE句で現在の述語を使用しても*有効です。オプティマイザは、空間索引を使用して、大まかに正しい領域内の候補マッチを見つけ、それをさらに絞り込み、「どこで使用するか」を指定して、非空間述語と一致する行をフィルタリングし、既存のロジックの精度を維持しながらパフォーマンスを向上させることができます。 Spatialは間違いなく進路です。 –

+0

元のクエリの式は、iirc 'ST_Distance'がhaversineではないので、ターゲット位置から距離xのバウンディングボックスを表すポリゴンを計算して返すストアド関数内で書き直すこともできます。 –

1

空間インデックスは、MySQLのバージョンによって異なります。私たちのサイトはlat/lonsも検索しますが、古いバージョンのMySQL(5.1-something)(空間インデックスなし)を使用しています。あなたの質問は我々のものと似ていますが、私たちはラジアンに基づいています。あなたの正確なニーズに応じて、あなたが持っているものからそれをかなり最適化することができます。ユーザへの実際の距離を表示するときにのみ終了時にそれを計算する - - 確かに、データベースクエリからドロップSQRT()

  1. 、それは行毎に計算する必要があり、また、「有する距離を二乗< 5 "〜" < 25 "となる。 Sqrtは高価で、計算する必要のない場所に簡単に移動できます。
  2. lat/lon '49 .1044302 'を引用符で囲まないようにしてください。これは厳密にはintであり、クエリの外側で緯度/経度タイプのチェックを行います。これは速度を上げることはありませんが、lat/lon変数の末尾に空白があるためにキャストが正しく行われないことがあります。
  3. 5を各方向の実際の緯度/ロット差の差+/5に変換して、制限範囲(そのままのボックス)を作成します。これをクエリの「どこ」部分に追加しますか?この制限により、実質的に縮小されたほぼ正確な結果行セットが得られます。基本的にlatおよびlonのxおよびy +/-範囲は結果の上限です - 対角線は結果とその距離をわずかに微妙にしか計算しません。
  4. 数学の多くをselectの外側に移動すると、テーブル全体をスキャンし、すべての行で計算された一時的なテーブルを作成して、その結果を得る必要があります。クエリの数学の多くは定数に変換することができます。
  5. 緯度/経度(コピー)の解像度を別のフィールドに下げて(おそらく10または100を逓倍してINTに変換して)、行削減(選択ボックス)をさらに高速化し、そのフィールドを+/-境界でどこで使用すれば、少なくとも鍵を使用できるようになります - mysqlはその結果を大幅に減らすことができます。

少なくともこれが私たちのやり方です。

+0

'49.1044302'は" int "ではありません。 「数字」があります。また、引用符は数値列を比較するときには関係ありません。 –

+0

バウンディングボックス(#3)は、この答えの中で最も生産的な部分です。あなたは 'INDEX(latitude)'または 'INDEX(longitude)'を持っていると仮定します。 –

+0

Rickが正しくありません。 「浮動小数点数」は「整数」ではありませんが、数値、好ましい整数インデックスの恩恵を受けるには、緯度または経度を掛けて精度を落とす必要があります。また、引用符は重要ですが、あなたが暗示している理由ではありません。スペースが何らかの形でlatまたはlong変数の文字列に変換された場合、 "$ var"はmysqlオプティマイザによって数値にキャストされません。引用符を残すことで、誤ったスペース(おそらくユーザー入力から)がSQL自体のスペースになり、無害になります。 "馬鹿のボタン"を押すのが速すぎないでください。 – Beracah

関連する問題