2012-05-10 16 views
4

は、私は3つのテーブルを持っています。MySQLのグループ化クエリの最適化

カテゴリのテーブルと最新のイベントstatus_idが「1」である記事の数が返されます。

私はこれまでのところ動作していますが、テーブルのサイズがかなり遅い(10秒)。これをもっと速くする方法があるのだろうかと思っています。すべてのテーブルは、私が知る限り、適切なインデックスを持っています。

SELECT c.id, 
     c.name, 
     SUM(CASE WHEN e.status_id = 1 THEN 1 ELSE 0 END) article_count 
FROM categories c 
LEFT JOIN articles a ON a.category_id = c.id 
LEFT JOIN (
    SELECT article_id, MAX(id) event_id 
    FROM article_events 
    GROUP BY article_id 
) most_recent ON most_recent.article_id = a.id 
LEFT JOIN article_events e ON most_recent.event_id = e.id 
GROUP BY c.id 

基本的に私はちょうどMAX(ID)の行に関連付けられているものを最初に見つかっSTATUS_IDは戻り、ないMAX(ID)と共にSTATUS_IDを求めているので、二倍イベントテーブルに参加しなければなりません。

これを改善する方法はありますか?それとも10秒で生きなければならないのですか?ありがとう!

編集:

ここ

だ私のクエリのEXPLAIN:

ID | select_type | table   | type | possible_keys | key   | key_len | ref     | rows | Extra 
--------------------------------------------------------------------------------------------------------------------------- 
1 | PRIMARY  | c    | index | NULL   | PRIMARY  | 4  | NULL     | 124044 | Using index; Using temporary; Using filesort 
1 | PRIMARY  | a    | ref | category_id | category_id | 4  | c.id     | 3  | 
1 | PRIMARY  | <derived2>  | ALL | NULL   | NULL  | NULL | NULL     | 6351 | 
1 | PRIMARY  | e    | eq_ref | PRIMARY  | PRIMARY  | 4  | most_recent.event_id | 1  | 
2 | DERIVED  | article_events | ALL | NULL   | NULL  | NULL | NULL     | 19743 | Using temporary; Using filesort 
+3

質問のために 'EXPLAIN ...'の出力をここに投稿してください。 – vyegorov

答えて

1

JOINを使用してサブクエリを削除できる場合は、派生テーブルでインデックスを使用できないため、JOINを使用したサブクエリがよく機能します。

SELECT c.id, 
     c.name, 
     COUNT(a1.article_id) AS article_count 
FROM categories c 
LEFT JOIN articles a ON a.category_id = c.id 
LEFT JOIN article_events ae1 
    ON ae1.article_id = a.id 
LEFT JOIN article_events ae2 
    ON ae2.article_id = a.id 
    AND ae2.id > a1.id 
WHERE ae2.id IS NULL 
GROUP BY c.id 

はあなたがテストするためにEXPLAINインデックスで実験し、使用したいが、ここでは私の推測ですよ(私はidフィールドが主キーと仮定しています、あなたはInnoDBテーブルを使用している):ここでは、サブクエリのないクエリです

categories: `name` 
articles: `category_id` 
article_events: (`article_id`, `id`) 
+0

これです。ありがとうございました。これまでに一番最近の行を取得するこの方法を見てきましたが、大きなテーブルではそれが遅いことを読んでいました。明らかにそうではない。クエリは0.0058秒で実行されます。 – Charles

0

それを試していませんでしたが、私は、これはデータベースのための仕事のビットを節約することを考えている:

SELECT ae.article_id AS ref_article_id, 
    MAX(ae.id) event_id, 
    ae.status_id, 
    (select a.category_id from articles a where a.id = ref_article_id) AS cat_id, 
    (select c.name from categories c where c.id = cat_id) AS cat_name 
FROM article_events 
GROUP BY ae.article_id 

012:

EDITを助け

・ホープ

ところで...結合は各行を通過しなければならないことを覚えておいてください。あなたが助けることができるなら、あなたは小さな端からあなたの選択を開始し、あなたの方法を働かせるべきです。この場合、クエリは100,000レコードを実行し、それぞれを結合してからそれらを100,000回再結合する必要があります。値がnullの場合でも、それらを通過する必要があります。

・ホープ、このすべては、あなたがテーブル全体を選択しているようcategories.id上のインデックスが、使用されているように私はしないでください...

0

に役立ちます。

てみランニング:

ANALYZE TABLE categories; 
ANALYZE TABLE article_events; 

と、クエリを再実行します。