MySql Filesort和临时按索引列排序而不是在pk上排序时

时间:2014-08-02 12:50:45

标签: mysql sql innodb

我真的想要做到这一点,但我无法弄清楚发生了什么。问题是,当连接中的索引列按顺序排序时,MySQL会进入filesort。

三张桌子:

CREATE TABLE IF NOT EXISTS articles (
 pk int unsigned NOT NULL AUTO_INCREMENT,
 id varchar(254) NOT NULL,
 title VARCHAR(128) DEFAULT NULL,
 text VARCHAR(4096) DEFAULT NULL,
 publicationTime DATETIME DEFAULT NULL,
 KEY publicationTime (publicationTime),
 PRIMARY KEY (pk)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

CREATE TABLE IF NOT EXISTS articles_channels (
 pk int unsigned NOT NULL AUTO_INCREMENT,
 article int unsigned NOT NULL,
 channel int unsigned NOT NULL,
 UNIQUE KEY ac (article,channel),
 PRIMARY KEY(pk)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;


CREATE TABLE IF NOT EXISTS channels (
 pk INT UNSIGNED NOT NULL AUTO_INCREMENT,
 id VARCHAR(64) NOT NULL,
 UNIQUE KEY id (id),
 PRIMARY KEY (pk)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

通过articles_channels表将文章附加到零个或多个频道。

查询的目的是查找包含在一组通道(S1)中的文章,并在另一组(S2)中排除,并按publicationTime desc排序。

例如:给我前10篇文章(发布时间)属于频道'c1'而不是('c2','c3')。

查询:

SELECT 
 SUM(case WHEN c.id IN ('c1') THEN 1 ELSE 0 END) as cin,
 SUM(case   WHEN c.id IN ('c2','c3') THEN 1 ELSE 0 END) as cout,
 a.publicationTime 
FROM articles a 
 LEFT JOIN articles_channels ac ON ac.article=a.pk 
 LEFT JOIN channels c ON ac.channel=c.pk 
 GROUP BY a.pk HAVING (cin>=1) AND cout=0 
 ORDER BY a.publicationTime DESC 
 LIMIT 1,10;

查询提供以下说明:

enter image description here

当我将ORDER BY a.publicationTime更改为ORDER BY a.pk时'使用临时,使用filesort'消失。我只是无法得到它。非常感谢任何帮助。

BR

的Niclas

如果它可以帮助任何人:问题的(一半)解决方案是在两个查询中拆分查询以避免fullscan进入磁盘。在全扫描中仅选择主键以允许它在内存中执行要便宜得多。从主表WHERE pk IN(pk1,pk2,pk3,..)执行第二个查询。问题仍然存在,但在我的情况下,它可以全部在内存中运行,大大提高了性能。

1 个答案:

答案 0 :(得分:0)

如果每篇文章只需要一行,这里有一种可能使用索引的替代方法:

SELECT a.publicationtime, cin, cout
FROM articles a join
     (select ac.article, sum(c.id in ('c1')) as cin, sum(c.id in ('c2', 'c3')) as cout
      from articles_channels ac
           channels c
           on ac.channel = c.pk
      group by ac.article
     ) ac
     on ac.article = a.pk and cin >= 1 and cout = 0
ORDER BY a.publicationTime DESC 
LIMIT 1, 10;

请注意,left join是不必要的,因为cincout上的条件。

如果这不起作用,那么使用相关子查询的版本很可能会使用索引。

编辑:

最后一次尝试是:

SELECT a.publicationtime,
       (select sum(c.id in ('c1')) as cin
        from articles_channels ac
             channels c
             on ac.channel = c.pk
        where ac.article = a.pk
       ) as cin,
       (select sum(c.id in ('c2', 'c3')) as cout
        from articles_channels ac
             channels c
             on ac.channel = c.pk
        where ac.article = a.pk
       ) as cout
FROM articles a 
HAVING ac.article = a.pk and cin >= 1 and cout = 0
ORDER BY a.publicationTime DESC 
LIMIT 1, 10;