2017-04-23 5 views
2

このコードは小さなテーブルでも機能しますが、180万レコードのテーブルがあり、検索時間は約12秒です。私はテーブルのbツリーインデックスをclient_idとcustomernameでPhpmyadmin経由で作成し、スピードの変更はしません。私のテーブルには116列が含まれています。私はテーブルを複数のテーブルに分割し、joinコマンドを使用する必要がありますか?アイブ氏は、joinコマンドを見たが、そう誰かが簡単な例100万を超えるレコードを持つMySqlテーブルをインデックスする方法

$stmt = $conn->prepare("SELECT *, (3959 * acos(cos(radians(:currentlat)) * cos(radians(lat)) * cos(radians(longitude) - radians(:currentlon)) 
+ sin(radians(:currentlat)) * sin(radians(lat)))) AS distance FROM $live_table 

WHERE (companyname like :name ) 

AND 

is_active != :active_switch 

HAVING distance < :mydistance ORDER BY client_id desc LIMIT $start, $limit"); 

$stmt->bindValue(':name' , "%$name%", PDO::PARAM_STR); 
$stmt->bindValue(':currentlat' , "$currentlat", PDO::PARAM_STR); 
$stmt->bindValue(':currentlon' , "$currentlon", PDO::PARAM_STR); 
$stmt->bindValue(':mydistance' , "$mydistance", PDO::PARAM_STR); 
$stmt->bindValue(':active_switch' , "$active_switch", PDO::PARAM_STR); 
+0

実際に私は何とかインデックスを作成する必要がある検索がたくさんある約15のフィールドを持っています。上記は私が上記の質問のために作ることができる最も簡単です。私は住所、市、州などのフィールドを持っています... –

+1

SQLの質問、特にクエリのパフォーマンスに関するこの提案のセットをお読みください。 http://meta.stackoverflow.com/a/271056/ –

+0

クエリごとに「3959」が変更されますか? – nogad

答えて

0

クエリが遅い重要な理由はHAVING DISTANCE < :mydistance句ですを与えることができればそれを前に使用したことがありません。これは、あなたのMySQLサーバにすべての行までの距離を計算させ、それをフィルタリングすることを強要し、おそらくメガロウテーブルの全テーブルスキャンを引き起こします。

おそらくWHERE句にバウンディングボックス計算を使用する必要があります。考え方は、あなたのテーブルから、南、北、東、または西にあまりにも遠くにある列を:mydistanceの範囲内に含まないようにすることです。あなたのクエリではlat列のインデックスを使用して多くの行を除外できるため、これは良い考えです。

... 
WHERE (companyname like :name ) 
AND 
is_active != :active_switch 
AND lat BETWEEN :currentlat - (:mydistance/69.0) 
      AND :currentlat + (:mydistance/69.0) 
AND long BETWEEN :currentlong - (:mydistance/(111.045 * COS(RADIANS(latpoint)))) 
      AND :currentlong + (:mydistance/(111.045 * COS(RADIANS(latpoint)))) 
HAVING distance < :mydistance ORDER BY client_id desc LIMIT $start, $limit 

このバウンディングボックス検索の過去記事はhereである:それはこのようになります。マジックナンバー69.0は、緯度ごとに約69マイルがあるという事実から来ています。 3959は、1ラジアン(57.3958度)の緯度のマイル数です。

ただし、このクエリを作成した方法によって、インデックスを使用して高速化することは困難です。 It's not sargable as written。あなたは

WHERE companyname = :name 
    AND is_active = :inactive_switch 
    AND lat BETWEEN ... 
    AND long BETWEEN ... 

としてあなたの基準を作り直すことができます場合は、(companyname, is_active, lat)上の複合インデックスは、クエリのパフォーマンスで奇跡的な改善を生成します。クエリプランナは、インデックスを適切な開始値latにランダムにアクセスしてから、最後に関連する値に順番にスキャンします。

多くの単一列インデックスを作成すると、一般にINSERTUPDATEのパフォーマンスに悪影響を及ぼし、SELECTクエリを高速化するのに役立たないことに注意してください。

SELECT *は、特に列が多く、ORDER BY ... LIMITパターンのテーブルでは、クエリのパフォーマンスに悪影響を及ぼすことにも注意してください。どうして? MySQLは、長い行を混乱させて並べ替える必要があります。 SELECTステートメントに実際に必要な列を表示するほうがはるかに優れています。

116列は非常に大きく、病理学的に多数の列が1つの表、特にメガロー表にあると言うものがあります。詳細を知らなくてもデータのリストラを推奨するのは難しいです。データベースの正規化というトピックを調べることができます。

+0

ありがとう、すごい説明と私が思ったもののようなものです。私はテーブルをもっとエフェクトにするために再設計する必要があります。これは私が今までに設計した最初のテーブルなので、良いテーブル構造で自分自身を教育する時間です。 –

0

クエリが長時間かかる主要な理由は、クエリでSin、Cos、ACos関数を使用することです。

ご存知のように:

  • 製品の操作は、和演算よりも約32倍遅くなります。 Sin、Cos、ACosは、積演算よりも約50倍遅い、すなわち総和演算よりも約1500倍遅い。

これらの計算には非常に時間がかかります。特に、これらの計算を何百万行も繰り返す場合は特にそうです。あなたが必要なもの

は次のとおりです。

  • は罪、コスしたい適切な精度で& ACOS関数の結果のテーブルを定義します。
  • 次に、これらの関数のいくつかのUDFを定義して、テーブルの最も近い結果に基づいてこれらの関数の結果を計算します。
  • Sin、Cos、ACosの組み込み関数ではなく、これらのUDFを使用します。
+0

浮動小数点ユニットなしでintel 286を使用している場合は、いくつかのコサインルックアップテーブルを作成します。しかし、おそらく現代のマシンを使用しており、浮動小数点ユニットは各距離計算で数100ナノ秒を消費します。ここでの問題は、距離計算のコストではなく、質問者のクエリーが書かれているように、メガロウテーブルのすべての行についてその計算を繰り返さなければならないという事実です。コストは計算ではなく、テーブル全体をスキャンするのはIOです。 UDFを構築するというあなたの提案は、尊重しながら問題を解決するために不必要で不十分です。 –

関連する問題