サブセレクトを使用してMySQLクエリがハングするのはなぜですか?
質問
次のクエリがハングします:(個別に実行されるサブクエリは問題ありませんが)
説明テーブルを適切に表示する方法がわかりません。誰かが私に言ったら、それをきれいにします。
select
sum(grades.points)) as p,
from assignments
left join grades using (assignmentID)
where gradeID IN
(select grades.gradeID
from assignments
left join grades using (assignmentID)
where ... grades.date <= '1255503600' AND grades.date >= '984902400'
group by assignmentID order by grades.date DESC);
問題は1年生のテーブルにあると思います...その数の行を持つALL型が原因のようです。すべてがインデックス化されています。
表を画像としてアップロードしました。フォーマットを正しく取得できませんでした: http://imgur.com/AjX34.png
コメンターは完全なwhere句が必要でした:
explain extended select count(assignments.assignmentID) as asscount, sum(TRIM(TRAILING '-' FROM grades.points)) as p, sum(assignments.points) as t
from assignments left join grades using (assignmentID)
where gradeID IN
(select grades.gradeID from assignments left join grades using (assignmentID) left join as_types on as_types.ID = assignments.type
where assignments.classID = '7815'
and (assignments.type = 30170 )
and grades.contactID = 7141
and grades.points REGEXP '^[-]?[0-9]+[-]?'
and grades.points != '-'
and grades.points != ''
and (grades.pointsposs IS NULL or grades.pointsposs = '')
and grades.date <= '1255503600'
AND grades.date >= '984902400'
group by assignmentID
order by grades.date DESC);
解決
Real Database(つまり、MySQLを除く任意のデータベースを使用しますが、Postgresを例として使用します)を使用してこのクエリを実行するとします:
SELECT * FROM ta WHERE aid IN (SELECT subquery)
実際のデータベースはサブクエリを見て、その行数を推定します:
- 行数が少ない場合(数百万未満など)
サブクエリを実行してから、IDのメモリ内ハッシュを作成します。これにより、IDも一意になります。これはIN()の機能です。
次に、taからプルされた行数がtaのごく一部である場合、適切なインデックスを使用して行をプルします。または、テーブルの大部分が選択されている場合、テーブル全体をスキャンし、ハッシュ内の各IDを検索します。これは非常に高速です。
- ただし、サブクエリの行数が非常に多い場合
データベースはおそらくマージJOINとして書き換え、サブクエリにSort + Uniqueを追加します。
ただし、MySQLを使用しています。この場合、これは何も行いません(テーブルの各行に対してサブクエリを再実行します)ので、1000年かかります。申し訳ありません。
他のヒント
「INの耐え難い遅さ」を参照してください: http://www.artfulsoftware.com/infotree/queries.php#568
非常に面倒ですが、:(みんなのおかげで)
SELECT *
FROM grades
LEFT JOIN assignments ON grades.assignmentID = assignments.assignmentID
RIGHT JOIN (
SELECT g.gradeID
FROM assignments a
LEFT JOIN grades g
USING ( assignmentID )
WHERE a.classID = '7815'
AND (
a.type =30170
)
AND g.contactID =7141
g.points
REGEXP '^[-]?[0-9]+[-]?'
AND g.points != '-'
AND g.points != ''
AND (
g.pointsposs IS NULL
OR g.pointsposs = ''
)
AND g.date <= '1255503600'
AND g.date >= '984902400'
GROUP BY assignmentID
ORDER BY g.date DESC
) AS t1 ON t1.gradeID = grades.gradeID
サブクエリを個別に実行したときに正常に動作する場合は、次のように、INではなくJOINを使用してみてください:
select count(assignments.assignmentID) as asscount, sum(TRIM(TRAILING '-' FROM grades.points)) as p, sum(assignments.points) as t
from assignments left join grades using (assignmentID)
join
(select grades.gradeID from assignments left join grades using (assignmentID) left join as_types on as_types.ID = assignments.type
where assignments.classID = '7815'
and (assignments.type = 30170 )
and grades.contactID = 7141
and grades.points REGEXP '^[-]?[0-9]+[-]?'
and grades.points != '-'
and grades.points != ''
and (grades.pointsposs IS NULL or grades.pointsposs = '')
and grades.date <= '1255503600'
AND grades.date >= '984902400'
group by assignmentID
order by grades.date DESC) using (gradeID);
あなたの質問に答えるのに十分な情報が本当にありません。そして、奇妙なwhere句の真ん中に...を置きました。関係するテーブルの大きさおよびインデックスは何ですか?
in句に含まれる用語が多すぎると、パフォーマンスが大幅に低下することがわかります。 inの使用を正しい結合に置き換えます。
初心者の場合、in句のテーブル as_types は使用されません。左に参加しても意味がないので、取り除いてください。
これにより、in句には、外部クエリからの割り当てと成績テーブルのみが残ります。変更の割り当てがどこにあるかは、明らかに外部クエリのwhere句に属します。すべてのwhere grades = whatever を成績への左結合のon句に移動する必要があります。
クエリを追跡するのは少し難しいですが、サブクエリはまったく必要ないと思われます。 クエリは基本的に次のように思われます:
SELECT FOO()
FROM assignments LEFT JOIN grades USING (assignmentID)
WHERE gradeID IN
(
SELECT grades.gradeID
FROM assignments LEFT JOIN grades USING (assignmentID)
WHERE your_conditions = TRUE
);
しかし、あなたはサブクエリのwhere句で本当に凝ったことをしていません。 もっと似ていると思う
SELECT FOO()
FROM assignments LEFT JOIN grades USING (assignmentID)
GROUP BY groupings
WHERE your_conditions_with_some_tweaks = TRUE;
同様に機能します。
ここで重要なロジックが欠落している場合は、コメントしてください。この投稿を編集/削除します。