2012-02-26 51 views
3

我試圖找到一種方法來加快緩慢(filesort)MySQL查詢。慢速查詢多個地方,並按子句排序

表:

categories (id, lft, rgt) 
questions (id, category_id, created_at, votes_up, votes_down) 

例子查詢:

SELECT * FROM questions q 
INNER JOIN categories c ON (c.id = q.category_id) 
WHERE c.lft > 1 AND c.rgt < 100 
ORDER BY q.created_at DESC, q.votes_up DESC, q.votes_down ASC 
LIMIT 4000, 20 

如果我刪除ORDER BY條款,速度非常快。我知道MySQL不喜歡同一個子句中的DESCASC命令,所以我嘗試將(created_at, votes_up)複合索引添加到questions表中,並從ORDER BY子句中刪除q.votes_down ASC。這沒有幫助,WHERE子句似乎妨礙了這裏的工作,因爲它按來自另一個表(categories)的列進行過濾。但是,即使它工作,它也不是很正確,因爲我確實需要q.votes_down ASC條件。

在這種情況下改善性能的策略是什麼?如果可能,我寧願避免重構表格。

編輯:

CREATE TABLE `categories` (
    `id` int(11) NOT NULL auto_increment, 
    `lft` int(11) NOT NULL, 
    `rgt` int(11) NOT NULL, 
    PRIMARY KEY (`id`), 
    KEY `lft_idx` (`lft`), 
    KEY `rgt_idx` (`rgt`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; 

CREATE TABLE `questions` (
    `id` int(11) NOT NULL auto_increment, 
    `category_id` int(11) NOT NULL, 
    `votes_up` int(11) NOT NULL default '0', 
    `votes_down` int(11) NOT NULL default '0', 
    `created_at` datetime NOT NULL, 
    PRIMARY KEY (`id`), 
    KEY `questions_FI_1` (`category_id`), 
    KEY `votes_up_idx` (`votes_up`), 
    KEY `votes_down_idx` (`votes_down`), 
    KEY `created_at_idx` (`created_at`), 
    CONSTRAINT `questions_FK_1` FOREIGN KEY (`category_id`) REFERENCES `categories` (`id`) ON DELETE CASCADE 
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; 

id select_type table type possible_keys   key  key_len ref     rows Extra 
1 SIMPLE  q  ALL questions_FI_1   NULL NULL NULL    31774 Using filesort 
1 SIMPLE  c  eq_ref PRIMARY,lft_idx,rgt_idx PRIMARY 4  ttt.q.category_id 1  Using where 
+0

您是否對所有列進行了索引? – BenOfTheNorth 2012-02-26 12:04:37

+1

您沒有爲2個表格發佈'SHOW CREATE TABLE'的輸出,也沒有發佈'EXPLAIN'的輸出。你也有'LIMIT 4000,20'。如果您需要幫助分析查詢,請發佈上述查詢的輸出。 – 2012-02-26 12:06:48

+0

@Ben Griffiths:所有列都有索引。 – Ree 2012-02-26 12:07:31

回答

0

嘗試選擇在您的查詢只需要什麼,而不是SELECT *

Why not to use SELECT * (ALL) in MySQL

+1

這與我的問題無關。 SELECT *在這裏是爲了縮短例子(我實際上並沒有使用它)。 – Ree 2012-02-26 12:50:46

+0

我不知道那是怎麼回事。能夠使用INDEX使事情變得更快。在你的陳述中有一個SELECT *使得這是不可能的。您的EXPLAIN正在展示這一點。 – conrad10781 2012-02-26 12:54:09

+0

所選列對索引沒有任何影響。 – Ree 2012-02-26 13:40:58

1

嘗試使用子查詢,以獲得所需的類別:

SELECT * FROM questions 
WHERE category_id IN (SELECT id FROM categories WHERE lft > 1 AND rgt < 100) 
ORDER BY created_at DESC, votes_up DESC, votes_down ASC 
LIMIT 4000, 20 
0

試着把條件,關於連接表int o ON子句:

SELECT * FROM questions q 
INNER JOIN categories c ON (c.id = q.category_id AND c.lft > 1 AND c.rgt < 100) 
ORDER BY q.created_at DESC, q.votes_up DESC, q.votes_down ASC 
LIMIT 4000, 20 
+0

我真的不知道爲什麼這應該有所幫助,但它可能與WHERE作爲放置所有表的條件的地方有關。也許它不能在該模式下使用連接表的索引。或者可能不是。我只能在這裏想出理由。 :)如果有人能分享這些知識,會很高興。 – Slava 2012-02-26 17:37:54

+0

Ree,你可能想看看這個也是另一種分頁結果的方式:http://stackoverflow.com/q/1243952/598472。然而,在任何列上用戶定義的排序看起來很棘手。在實施之前,您應該完全檢查是否值得花費所有努力來維護額外的層。 :) – Slava 2012-02-26 17:52:23