选择9. +秒。如何更好地重写?

时间:2018-06-05 19:56:07

标签: mysql sql

我有这个选择:

select t.id, c.user, t.title, pp.foto, t.data from topics t
inner join cadastro c on t.user = c.id
left join profile_picture pp on t.user = pp.user
left join (
    select c.topic, MAX(c.data) cdata from comments c
    group by c.topic
)c on t.id = c.topic
where t.community = ?
order by ifnull(cdata, t.data) desc
limit 15

我想选择主题并根据主题评论的日期或日期对其进行排序(如果有评论)。

不幸的是,这需要超过9秒。

enter image description here 我不认为这里的问题是索引,而是我编写选择本身的方式。

`topics` (
  `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
  `user` INT(11) UNSIGNED NOT NULL,
  `title` varchar(100) NOT NULL,
  `description` varchar(1000),
  `community` INT(11) UNSIGNED NOT NULL,
  `data` datetime NOT NULL,
  `ip` varchar(20),
  PRIMARY KEY (`id`),
  FOREIGN KEY (`user`) REFERENCES cadastro (`id`),
  FOREIGN KEY (`community`) REFERENCES discussion (`id`)
)

`comments` (
  `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
  `user` INT(11) UNSIGNED NOT NULL,
  `comment` varchar(1000) NOT NULL,
  `topic` INT(11) UNSIGNED NOT NULL,
  `data` datetime NOT NULL,
  `ip` varchar(20),
  `delete` tinyint(1) NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`),
  FOREIGN KEY (`user`) REFERENCES cadastro (`id`),
  FOREIGN KEY (`topic`) REFERENCES topics (`id`)
)

3 个答案:

答案 0 :(得分:3)

您的EXPLAIN给了您强烈的暗示。该结果的第一行表示,using temporary, using filesort暗示它没有使用索引。

可以通过添加索引和删除一些条件来改进此查询,但我认为在这种情况下存在更好的解决方案。

为什么不向topics添加一个新列,指示上次添加评论的时间? (比如last_modified)。每次添加评论时,只需更新该主题的列。

它实际上是非规范化的。我认为这是一个有效的用例,它总是比修复这个凌乱的查询更快。

答案 1 :(得分:3)

您正在每个查询的表comments上执行全表扫描。它有多少行?至少创建以下索引:

comments (topic, data);

每次都避免阅读整个表格。

答案 2 :(得分:2)

我知道你说你不认为问题是索引,但是10次中有9次我遇到了这个问题,而这正是它的问题。

确保在查询中使用的每个表上创建索引,并包括在连接中指定的列。

另外,正如NiVeR所说,不要多次使用相同的别名。

这是对该查询的重构,不确定我是否混淆或错过了一个或两个列别名/别名。

select t.id, c.user, t.title, pp.foto, t.data from topics t
inner join cadastro c on t.user = c.id
left join profile_picture pp on t.user = pp.user
left join (
    select com.topic, MAX(com.data) comdata from comments com
    group by com.topic
)com1 on t.id = com1.topic
where t.community = ?
order by ifnull(com1.comdata, t.data) desc
limit 15