私は以下のようにMysqlでクエリを実行しました:
EXPLAIN
SELECT *
FROM(
SELECT * # Select Number 2
FROM post
WHERE parentid = 13
ORDER BY time, id
LIMIT 1, 10
) post13_childs
JOIN post post13_childs_childs
ON post13_childs_childs.parentid = post13_childs.id
結果は次のとおりです。
id |select_type |table |type |possible_keys |key |key_len |ref |rows |Extra
1 |PRIMARY |<derived2> |ALL | NULL | NULL |NULL |NULL |10 |
1 |PRIMARY |post13_childs_childs|ref |parentid |parentid |9 |post13_childs.id |10 |Using where
2 |DERIVED |post |ALL |parentid |parentid |9 | |153153 |Using where; Using filesort
これは、インデックスparentid
を使用したが、ALL
と153153
のためにすべての行をスキャンしたことを意味します。インデックスがFull Scannig
をしないのに役立たなかったのはなぜですか?
ただし、派生クエリを実行すると(#2を選択)単独以下のようになります:
Explain
SELECT * FROM post
WHERE parentid=13
ORDER BY time , id
LIMIT 1,10
結果が望まれます:
id |select_type |table |type |possible_keys |key |key_len |ref |rows |Extra
1 |SIMPLE |post |ref |parentid |parentid |9 |const|41 |Using where; Using filesort
テーブルpost
には次のインデックスがあります。
合計行数-> 141280。13
の子の数(parentid=13
)-> 4111523
の子の数-> 10119
(parent,time,id)
のインデックスを追加すると、最初のクエリの問題は13
-> 40行のexplin出力によって解決されます。type:ref
および11523
-> 19538行の場合、type:ref !!!これは、最初の10行を制限しているときに、11423
のすべての子行が検査されることを意味します。
サブクエリ:
SELECT * # Select Number 2
FROM post
WHERE parentid = 13
ORDER BY time, id
LIMIT 1, 10;
これは、3つの列に加えて、残りのすべての列について明示的に言及しています。3つのインデックスがあります。使用方法は次のとおりです。
order by
句で言及されていますが、これは2番目の条件ですwhere
句を満たすために使用できます。ただし、正しいデータが取得された後は、明示的に並べ替える必要があります。parentid
の条件が満たされているかどうかを行ごとに確認する必要があります。最適化が難しい理由を紹介するだけです。少量のデータがある場合(たとえば、テーブルが1ページまたは2ページに収まる場合)、全表スキャンとそれに続くソートでおそらく問題ありません。ほとんどのparentid
値が13
である場合、2番目のインデックスは最悪の場合である可能性があります。テーブルがメモリに収まらない場合、3番目は非常に遅くなります(ページスラッシングと呼ばれるもの)。
このサブクエリの正しいインデックスは、where
句を満たし、順序付けが可能なインデックスです。そのインデックスはparentid, time, id
です。これはカバーインデックスではありません(これらがテーブルのすべての列でない限り)。ただし、limit
句があるため、実際の行へのヒット数は10に減るはずです。
完全なクエリの場合、parentid
にインデックスが必要であることに注意してください。そして、幸いなことに、parentid, time, id
のインデックスはそのようなインデックスとしてカウントされます。したがって、そのインデックスを削除できます。 time, id
インデックスは、他のクエリで必要な場合を除いて、おそらく必要ありません。
また、クエリは、自分自身に「子供」がいる「子供」のみをフィルタリングしています。行が返されない可能性は十分にあります。本当にleft outer join
を意図していますか?
最後のコメントとして。このクエリは、実際のクエリを単純化したものだと思います。クエリは2つのテーブルからすべての列をプルしています-そしてそれらの2つのテーブルは同じです。つまり、同一のテーブルから重複する列名を取得します。列をより適切に定義するには、列エイリアスが必要です。
どのインデックスにも助けられないORDERBYを実行すると、定期的にパフォーマンスが低下する可能性があります。内部クエリの場合、WHERE句とORDER BY句の両方がインデックスを利用できるように、(parentID、time、id)にカバーインデックスを設定します。 parentIDは結合のあとがきの基礎でもあるため、そこに移動して非常に高速にすることをお勧めします。