2012-02-26 52 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