回答:
LIMIT
パフォーマンスを向上させるために利用したい場合は、
LIMIT
以前に使用JOIN
これらの原則は、それらを調整することができれば、長い道のりを行くことができます。
私はこのYouTubeビデオを見てこれらの概念を学びました(フランスのアクセントを注意深く聞いてください)
:私はいくつかのテーブルから上位40記事得ることについて非常に厳しいStackOverflowの質問に答えるために、これらの概念を使用2011年5月12日を表に参加からの単一の行の取得を。
ではその質問への私の答え(2011年5月16日)、私は次のクエリを書き、それを徹底的にテストしました:
SELECT
AAA.author_id,
AAA.date_created,
IFNULL(BBB.title,'<NO_TITLE>') title,
IFNULL(CCC.filename,'<NO-IMAGE>') filename,
IFNULL(CCC.date_added,'<NO-IMAGE-DATE>') image_date
FROM
(
SELECT
AA.id,
AA.date_added,
BB.author_id,
BB.date_created
FROM
(
SELECT
A.id,IFNULL(MAX(B.date_added),'1900-01-01 00:00:00') date_added
FROM (SELECT id FROM articles ORDER BY date_created DESC LIMIT 40) A
LEFT JOIN article_images B ON A.id = B.article_id
GROUP BY A.id
) AA
INNER JOIN articles BB USING (id)
) AAA
LEFT JOIN article_contents BBB ON AAA.id=BBB.article_id
LEFT JOIN article_images CCC
ON (AAA.id=CCC.article_id AND AAA.date_added=CCC.date_added)
ORDER BY AAA.date_created DESC;
クエリの行に注意してください LIMIT
FROM (SELECT id FROM articles ORDER BY date_created DESC LIMIT 40) A
このサブクエリは、3レベルの深さに埋め込まれています。これにより、を使用して最新の40件の記事を取得できましたLIMIT
。その後、必要なJOINを実行しました。
LIMIT
サブクエリ内では常にあるため、インデックスのカーディナリティ、データ内容、およびからの結果セットのサイズの答えではないかもしれませんLIMIT
。すべての「アヒルが並んでいる」場合(クエリについて4つの原則を念頭に置いてください)、驚くほど良い結果が得られます。LIMIT
キーのみを収集して行う場合は、クエリをできるだけ単純にします。(A [LEFT] JOIN B) LIMIT 100
と等価ですか(A LIMIT 100) [LEFT] JOIN (B LIMIT 100)
?ここ[LEFT] JOIN
で、外部結合または内部結合を意味します
(A LIMIT 100) [LEFT] JOIN B
ます。アイデアはLIMIT
、結果セットのサイズをできるだけ早く決定するために使用することです。私はまた、使用LEFT JOIN
の代わりをINNER JOIN
するのでLEFT JOIN
、左側のキーの順序を保持します。
(A LEFT JOIN B) GROUP BY A.pk LIMIT 100
なります。通常、次のように書き直すことができます(A LIMIT 100) LEFT JOIN B GROUP BY A.pk
(ここではINNER JOINはなく、内部結合は同等ではありません)。Rolandoの例はまさにそのような場合です。
クエリが実行されると、最初にいくつかの演算子で構成されるプランに変換されます。演算子には、ブロックと非ブロックの2つの基本タイプがあります。Non-Blocking Operatorは、要求された各行の子から、1つまたは複数の行を取得します。一方、ブロッキングオペレーターは、出力を生成する前に、すべての子の行セット全体を読み込んで処理する必要があります。
ソートは典型的なブロッキングオペレーターです。そのため、select by order byは制限からあまりメリットがありません。ただし、必要なメモリが少なく、limit句が指定されている場合はより高速なソートアルゴリズムを利用できるRDBMSがあります。この場合、現在の最初のn行を格納し、以前の行が発生したときにそれらをメモリから移動するだけで十分です。これにより、パフォーマンスが大幅に向上する可能性があります。ただし、MySQLにその機能があるかどうかは100%わかりません。
どちらの方法でも、最初の出力行を生成する前に、制限ソートでも入力行セット全体を処理する必要があります。このアルゴリズムを実装すると、ソートを高速化できますが、クエリの残りの部分が最もコストのかかる部分である場合は、提供された制限のため、合計実行時間は大幅に改善されません。
GROUP BY
ブロッキングオペレーターが含まれない計画につながる可能性があります。
私の場合、理由が(まだ)わからなくても、はいと言えます。
SELECT g0_.id AS id_0, COUNT(a1_.id_tarifs) AS sclr_1
FROM groupe_jardinerie g0_
INNER JOIN articles_tarifs a1_
ON (a1_.groupe_jardinerie_id = g0_.id)
WHERE g0_.centrale_id = 511
AND a1_.date_fin_tarif >= '2018-01-29 10:46:35'
GROUP BY g0_.id;
(result set)
8 rows in set (**18.14 sec**)
時間に注意してください:18秒。大きな制限付きの同じリクエスト:
SELECT g0_.id AS id_0, COUNT(a1_.id_tarifs) AS sclr_1
FROM groupe_jardinerie g0_
INNER JOIN articles_tarifs a1_
ON (a1_.groupe_jardinerie_id = g0_.id)
WHERE g0_.centrale_id = 511
AND a1_.date_fin_tarif >= '2018-01-29 10:46:35'
GROUP BY g0_.id
LIMIT 100000000000;
(exact same result set)
8 rows in set (**1.32 sec**)
10倍以上速く!!!
EXPLAINは、両方のリクエストで同じ結果になります。
+----+-------------+-------+------------+--------+---------------------------------------------------+---------+---------+------------------------------+--------+----------+----------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+--------+---------------------------------------------------+---------+---------+------------------------------+--------+----------+----------------------------------------------+
| 1 | SIMPLE | a1_ | NULL | ALL | IDX_438010BBC10784EF | NULL | NULL | NULL | 795135 | 33.33 | Using where; Using temporary; Using filesort |
| 1 | SIMPLE | g0_ | NULL | eq_ref | PRIMARY,IDX_9CA5CF6758A1D71F,IDX_9CA5CF67670C757F | PRIMARY | 4 | phs.a1_.groupe_jardinerie_id | 1 | 50.00 | Using where |
+----+-------------+-------+------------+--------+---------------------------------------------------+---------+---------+------------------------------+--------+----------+----------------------------------------------+
LIMITは結果セットを制限するためにのみ干渉する必要があります(つまり、LIMIT 4を実行すると、上記の結果セットの最初の4行のみが取得されます)。
LIMIT
。最初のクエリは18秒で実行され、結果セットが返されます。最初のクエリにより、2番目のクエリのすべてのデータはすでにInnoDBバッファープールにキャッシュされているため、当然、2番目のクエリはより高速である必要があります。クエリでも、同じ結果が得られます。。より良い結果をLIMIT
得るLIMIT
にはJOIN
、1)前に、2)ソート順でのLIMIT ASC
またはDESC
。
LIMIT
効率を向上させることができる:最適化LIMITクエリを