Index/Keyがないにもかかわらずフィールドを変更するときにMySQL非常に遅いクエリ
質問
これは私にとって非常に紛らわしい問題です。野球統計でいっぱいのデータベースがあります。このクエリを実行してください:
SELECT * FROM hits
JOIN stadiums ON stadiums.gameName = hits.gameName
JOIN players ON (players.gameName = hits.gameName AND players.id = hits.batter)
JOIN games ON games.gameName = hits.gameName
WHERE games.type = 'R'
LIMIT 50
戻り値:
/* 0 rows affected, 50 rows found. Duration for 1 query: 0.218 sec. */
しかし、このクエリを実行してください:
SELECT * FROM hits
JOIN stadiums ON stadiums.gameName = hits.gameName
JOIN players ON (players.gameName = hits.gameName AND players.id = hits.batter)
JOIN games ON games.gameName = hits.gameName
WHERE games.leagueLevel = 'mlb'
LIMIT 50
長い間ハングします。ゲームテーブルのインデックスはGames.gamenameのみであり、他には何もありません。
ゲームからの個別のタイプを選択すると、1つのヌルを含む8つのシングルキャラクターの行(Varchar 1)が与えられます。
SELECT GAMESの明確なLeagueLevelは、1つのnullを含む6つの3文字の列(Varchar 5)を提供します。
2番目のクエリが非常に遅くなる理由はわかりませんが、最初のクエリは正常に実行されます。
ご協力いただきありがとうございます。
解決
視点#1:あなたは多くの人が列の値の母集団を見る必要があります
SELECT COUNT(1) rowount,type FROM games GROUP BY type WITH ROLLUP;
SELECT COUNT(1) rowcount,leaguelevel FROM games GROUP BY leaguelevel WITH ROLLUP;
あなたの質問から、私は2つのことを集めます:
- Type = 'r'のゲームの行数は、ゲームテーブルの行数に対して低い数でなければなりません。
- LeagueLevel = 'MLB'を使用したゲームの行数は、ゲームテーブルの行数に対して高い数(テーブルの5%を超える)でなければなりません。 (5%は、クエリオプティマイザーの目には心のルール数です)
視点#2:このクエリをリファクタリングする必要があるかもしれません
すべての結合が完了した後、クエリがWhere部分を実行することに注意してください。場所を早めに実行できる場合は、時間を短縮するのに役立ちます。このようなクエリを再編成してみてください:
SELECT * FROM hits
JOIN stadiums ON stadiums.gameName = hits.gameName
JOIN players ON (players.gameName = hits.gameName AND players.id = hits.batter)
JOIN (SELECT * FROM games WHERE leagueLevel = 'mlb') games
ON games.gameName = hits.gameName
LIMIT 50;
視点#3:本当に必要な列のみを取得します
Select *があり、4つのテーブル(ヒット、スタジアム、プレイヤー、ゲーム)があります。特に4つのテーブルすべてからGameName列をドラッグするときに、クエリにドラッグするために多くの重複データがあります。
クエリを再編成して、1つのGameName列のみを持参する必要があります。
SELECT hits.gameName,hits.*,players.*,staduims.*,games.* FROM hits
JOIN stadiums ON stadiums.gameName = hits.gameName
JOIN players ON (players.gameName = hits.gameName AND players.id = hits.batter)
JOIN (SELECT * FROM games WHERE leagueLevel = 'mlb') games
ON games.gameName = hits.gameName
LIMIT 50;
さらに、ヒットテーブルのすべての列が必要ない場合は、アクセスできる列のみを含めます。プレイヤー、スタジアム、ゲームにも同じことが言えます。
言い換えれば、例として、プレーヤーテーブルのプレイ名のみが必要な場合は、プレーヤーは必要ありません。*選択。 player.playernameだけが必要です。
視点#4:LeagueLevel列にインデックスする必要があるかもしれません
必要なインデックスを作成するには、以下を実行する必要があります。
ALTER TABLE games ADD INDEX (leagueLevel);
そうする前に、これを実行してください
SELECT COUNT(1) rowcount,leaguelevel FROM games GROUP BY leaguelevel WITH ROLLUP;
カウントがテーブルの5%を超えるLeagueLevelの値は、MySQLクエリオプティマイザーがインデックスを使用しないようにします。
他のヒント
2番目のクエリが非常に遅くなる理由はわかりませんが、最初のクエリは正常に実行されます。
あなたは一人ではありません - 特定の理由とクエリの実行のどこに深く掘り下げなければならないことは非常に一般的です。
あなたが利用できるツールを使用することを学ぶ必要があります explain
と explain extended
. 。あなたがどのように乗るか教えてください...
あなたは最初のクエリを速く戻すことができるほど幸運です。 @Jack Douglasに同意し、説明を使用し、必要なインデックスを追加し、両方のクエリがはるかに改善されるまで繰り返します。