簡體   English   中英

MySQL分組查詢優化

[英]MySQL grouping query optimization

我有三個表:類別,文章和article_events,具有以下結構

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

基本上我必須兩次加入事件表,因為要求status_id和MAX(id)只返回它找到的第一個status_id,而不是與MAX(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

如果可以使用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`)

沒試過,但我想這會為數據庫節省一些工作:

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,它仍然必須經歷這些。

希望這一切都有幫助......

我不喜歡使用categories.id上的索引,因為你選擇了整個表。

試試跑步:

ANALYZE TABLE categories;
ANALYZE TABLE article_events;

並重新運行查詢。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM