4

次の構造を持つ、categorys、articles、article_events の 3 つのテーブルがあります。

categories: id, name                        (100,000 rows)
articles: id, category_id                   (6000 rows)
article_events: id, article_id, status_id   (20,000 rows)

各記事行の最上位の article_events.id は、各記事の現在のステータスを示します。

カテゴリのテーブルと、最新のイベント 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 が返されるため、基本的にイベント テーブルに 2 回参加する必要があります。

これを改善する方法はありますか?それとも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
4

3 に答える 3

1

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`)
于 2012-05-10T19:11:14.513 に答える
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

それが役立つことを願っています

編集:

ところで...結合は各行を通過する必要があることに注意してください。そのため、選択を小さな端から開始し、できる場合は上に向かって作業する必要があります。この場合、クエリは 100,000 レコードを実行し、それぞれを結合し、それらの 100,000 レコードを何度も結合する必要があります。値が null の場合でも、それらを処理する必要があります。

これがすべて役立つことを願っています...

于 2012-05-10T18:11:06.727 に答える
0

categories.idテーブル全体を選択しているため、インデックスが使用されるのは好きではありません。

実行してみてください:

ANALYZE TABLE categories;
ANALYZE TABLE article_events;

クエリを再実行します。

于 2012-05-10T18:20:09.547 に答える