web-dev-qa-db-ja.com

LEFT JOINサブクエリを使用してMySQL SELECTを最適化する

LEFT JOINとサブクエリを組み合わせたクエリがあります。データセットは非常に大きく、ステートメントの実行時間は70秒以上です。

SELECT
    s.siblings,
    l.id
FROM
    `list` l
        INNER JOIN
    child c ON c.id = l.child_id
        INNER JOIN
    parent p ON p.id = c.parent_id
    LEFT JOIN (
      SELECT COUNT(c.id) AS siblings, c.id, c.parent_id
      FROM child c
      GROUP BY c.id
    ) AS s ON s.parent_id = c.parent_id AND s.id != c.id
WHERE
    l.country = 1
GROUP BY l.id, s.siblings
ORDER BY l.dateadded

このクエリは、国のすべてのリストを返す必要があります。各リストは、固有の赤ちゃんに固有のものです。リストごとに、同じ親を持つ子の数を返したいと思います。

LEFT JOINサブクエリを削除すると、フェッチ時間は0.1秒になります。クエリをより効率的にする方法はありますか?

2
xylar

クエリが遅い主な理由は、サブクエリの結合です。これはインデックスを使用しません。次に、派生テーブル(サブクエリ)と結合するだけでなく、サブクエリ列に基づいて合計結果をグループ化します-GROUP BY l.id, s.Siblings

この場合、次のことが役立ちます。

  • サブクエリから一時テーブルを作成し、正しいparent_idを返すためのサブクエリを含めることもできます
  • このテーブルにインデックスを作成します
  • 結合で一時テーブルを使用する
  • 一時テーブルをドロップ

これはバリアントを持つ可能性がありますが、多くの場合、結合を使用した複雑なサブクエリのセットよりも高速で、サーバーの負荷が低くなります。

2
a_vlad

クエリには多くの不必要な複雑な機能があります。さらに、派生テーブルの_GROUP BY c.id_(child (id)がそのテーブルの主キーであると想定)は完全に冗長に見えます。クエリの結果は、siblings列では常に_1_である必要があります。これは、おそらく望ましい結果ではありません。

これはあなたが望むことをする(兄弟の数を見つける)はるかに簡単な方法で:

_SELECT
    s.cnt - 1 AS siblings,
    l.id
FROM
    `list` AS l
        INNER JOIN
    child AS c ON c.id = l.child_id
        INNER JOIN 
    ( SELECT c.parent_id, COUNT(*) AS cnt
      FROM child AS c
      GROUP BY c.parent_id
    ) AS s ON s.parent_id = c.parent_id
WHERE
    l.country = 1
ORDER BY l.dateadded ;
_
1
ypercubeᵀᴹ