MAX()によるMySQLクエリの最適化

以前に尋ねられているが、何らかの方法がある場合は、このクエリを最適化してより高速に実行することができます。分では約2秒かかりますが、莫大な量ではありませんが、私のサイトで最も遅いクエリですが、他のクエリでは0.5秒もかかりません。

ここに私の質問です:

SELECT SQL_CALC_FOUND_ROWS MAX(images.id) AS maxID, celebrity.* FROM images
JOIN celebrity ON images.celeb_id = celebrity.id
GROUP BY images.celeb_id
ORDER BY maxID DESC
LIMIT 0,20

ここに説明があります:

1 SIMPLE celebrity ALL PRIMARY NULL NULL NULL 536 Using temporary; Using filesort
1 SIMPLE images ref celeb_id celeb_id 4 celeborama_ignite.celebrity.id 191

私はこのクエリのパフォーマンスをさらに向上させる方法を失っています。私はMySQLに精通しているわけではありませんが、MAX()によって作成されたデータをソートしてインデックスがないため、速度が遅いことがわかります。私はそれが私に必要な結果を与えるので、私はそれを並べ替えることはできませんが、私はそれがクエリの速度を遅らせることを防ぐために何かできますか?

ありがとう。

3
有名人テーブルの行数はいくつですか?
追加された 著者 ypercubeᵀᴹ,
有名人テーブルの行数は536行ですが、 images テーブルの行数は103,411です
追加された 著者 Vunus,

5 答え

本当に高速なソリューションが必要な場合は、実行時にそのようなクエリを実行しないでください。

有名人の表に追加フィールド last_image_id を作成し、新しい画像をアップロードしたときにそのフィールドを更新するだけです(トリガーやアプリケーションロジックによって問題はありません)

2
追加された
ハハ。最も簡単な解決策は常に私が推測する最高のものです。ありがとう、これはおそらく最善の方法ですが、新しいフィールドを実装するまで他の人の提案を使わなければならないかもしれません。
追加された 著者 Vunus,

私はこの方法で最新のイメージを得るでしょう:

SElECT c.*, i.id AS image_id
FROM celebrity c
JOIN images i ON i.celeb_id = c.id
LEFT OUTER JOIN images i2 ON i2.celeb_id = c.id AND i2.id > i.id
WHERE i2.id IS NULL
ORDER BY image_id DESC
LIMIT 0,20;

言い換えれば、 i.id よりも高いIDを持つ同じ有名人の行 i2 を見つけようとします。外部結合が一致するものを見つけられなかった場合、 i.id は指定された有名人の最大イメージIDでなければなりません。

SQL_CALC_FOUND_ROWSを使用すると、クエリの実行が非常に遅くなる可能性があります。私は、SQL_CALC_FOUND_ROWSを削除するだけで、クエリが200倍速く実行された場合があります(しかし、それはテーブルに依存するため、両方の方法をテストする必要があります)。

SQL_CALC_FOUND_ROWSに相当するものが必要な場合は、別のクエリを実行してください:

SELECT COUNT(*) FROM celebrity;
1
追加された
ありがとうございます。しかし、残念ながらnewtoverのクエリは私のために速く走っているようです。他のケースでは、SQL_CALCはそれほど大きな違いはありませんでしたが、この記事では、ページングクエリを別々に行うことにスワップしました。 :)
追加された 著者 Vunus,

MYSQL doesn't perform so good with joins. i would recommend to dividing your query in two. that is in first query select the Celeb and then select image. Simply avoid joins. Check out this link - http://phpadvent.org/2011/a-stitch-in-time-saves-nine-by-paul-jones

0
追加された
申し訳ありませんが、それはMySQL 5.3での私の経験です。複数のクエリが実際にパフォーマンスを向上させます。
追加された 著者 Uday Sawant,
RDBMSでの結合を避けるようにアドバイスすることは、人々に自動車を運転するよう指示するのと同じですが、決して歯車を変更することはありません。
追加された 著者 ypercubeᵀᴹ,
それはまったくナンセンスです。
追加された 著者 Bill Karwin,
クエリの分割がより簡単で簡単に最適化されることがあります。しかし、人々に「単純に結合を避ける」というようなブランケットルールを伝えることは誤解を招きます。 for ループは、同じコードを何度も何度も実行しているため、非効率的であることを伝えるようなものです。
追加された 著者 Bill Karwin,
合意した私はクエリを分割する必要がある場合がありますが、すべての結合が悪いわけではありません。
追加された 著者 Vunus,

GROUP BY celeb_id タグは、 images (celeb_id、id)に複合インデックスが必要です(MyISAMテーブルの場合) MAX(id)はこのインデックスを使用できます。

But with big tables, you'll probably have to follow @zerkms' advice and add a new column in table celebrity

0
追加された
SELECT STRAIGHT_JOIN *
FROM (
  SELECT MAX(id) as maxID, celeb_id as id
  FROM images
  GROUP BY celeb_id
  ORDER by maxID DESC
  LIMIT 0, 20) as ids
JOIN celebrity USING (id);

クエリでは行番号の事前計算は許可されませんが、

SELECT COUNT(DISTINCT celeb_id)
FROM images;

または(各有名人がイメージを持っている場合でも)

SELECT COUNT(*) FROM celebrity;

クエリキャッシュによって簡単にキャッシュされるため、コストがかかりません(スイッチがオフになっていない場合)。

0
追加された
InnoDBインデックスにはすでにPKが追加されているため、IDの末尾にIDを追加する必要はありません。
追加された 著者 Bill Karwin,
@Bill、hm、それは知っておくと便利です!
追加された 著者 newtover,
これは私にとって最速のクエリのようです。私は前に私の1.5~2秒よりもずっと速い平均0.24秒を得る。 Precalcはページネーションのみに使用されるので、問題を起こさずに別のクエリに分割することができます。ありがとう。
追加された 著者 Vunus,