我真的想要做到这一点,但我无法弄清楚发生了什么。问题是,当连接中的索引列按顺序排序时,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;
查询提供以下说明:
当我将ORDER BY a.publicationTime更改为ORDER BY a.pk时'使用临时,使用filesort'消失。我只是无法得到它。非常感谢任何帮助。
BR
的Niclas
如果它可以帮助任何人:问题的(一半)解决方案是在两个查询中拆分查询以避免fullscan进入磁盘。在全扫描中仅选择主键以允许它在内存中执行要便宜得多。从主表WHERE pk IN(pk1,pk2,pk3,..)执行第二个查询。问题仍然存在,但在我的情况下,它可以全部在内存中运行,大大提高了性能。
答案 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
是不必要的,因为cin
和cout
上的条件。
如果这不起作用,那么使用相关子查询的版本很可能会使用索引。
编辑:
最后一次尝试是:
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;