2010-09-14 78 views
4

有人可以解释为什么由子查询添加组,使此查询需要很长时间(30secs):我该如何优化这个MySQL查询?

SELECT * 
FROM aggregate_songlist AS a 
INNER JOIN musical_works AS m 
ON a.musical_work_id = m.id 
WHERE m.genre='rock' AND m.id NOT IN 
(SELECT sources.musical_work_id FROM sources GROUP BY sources.musical_work_id HAVING COUNT(sources.musical_work_id) > 8) 

如果我“按组”中删除(和增加子查询的结果),它需要0.07秒:

SELECT * 
FROM aggregate_songlist AS a 
INNER JOIN musical_works AS m 
ON a.musical_work_id = m.id 
WHERE m.genre='rock' AND m.id NOT IN 
(SELECT sources.musical_work_id FROM sources) 

在子查询中没有外部引用,所以它只能执行一次,对吗?自行执行:

SELECT sources.musical_work_id FROM sources GROUP BY sources.musical_work_id HAVING COUNT(sources.musical_work_id) > 8 

只需要0.01秒。

任何解释?有关如何改变它的任何建议?

+0

是什么的'SOURCES'表的表引擎?我最近在读,MyISAM在InnoDB中用GROUP BY不能很好地执行。 – 2010-09-14 20:16:26

+1

哪些列有索引? – ikanobori 2010-09-14 20:19:23

+0

将这个组逐具有-计数-GT-8中一个临时表查询,并看到超查询的行为方式。 – 2010-09-14 20:20:17

回答

6

有子查询没有外部引用,所以它应该只执行一次,对吧?

你会这样想,但没有。如果你看看EXPLAIN,你会发现子查询被称为“依赖子查询”而不是“子查询”。这意味着它每次都会重新执行。这是MySQL 5.0中的known bug,并在MySQL 6.0中修复。

要解决它,你可以用其他的方法之一来检查,如果行没有在另一个表中。三种常用的方法不是IN,不存在,而且LEFT JOIN ... WHERE ...是NULL,所以你仍然有两个选择。

+0

谢谢。一个不存在的伎俩。 – user447736 2010-09-15 03:14:43

2

的NOT IN可能是您的问题。试着加入它,而不是(你有前后翻页HAVING子句):[!更新,以反映@马克Byers的评论,谢谢]

SELECT * 
FROM aggregate_songlist AS a 
INNER JOIN musical_works AS m 
ON a.musical_work_id = m.id 
LEFT JOIN (
     SELECT sources.musical_work_id FROM sources 
     GROUP BY sources.musical_work_id 
     HAVING COUNT(sources.musical_work_id) <= 8) AS t 
ON m.id = t.musical_work_id 
WHERE m.genre='rock' AND t IS NULL 

+0

这将不会返回存在于'一个JOIN M'但在'source'完全不存在的行。使用'LEFT JOIN ... WHERE ... IS NULL'会解决这个问题。 – 2010-09-14 20:43:03

+0

@Mark Byers:谢谢,我更新了我的答案以反映您的建议。 – 2010-09-14 21:18:30

0
SELECT * 
FROM 
aggregate_songlist AS a 
INNER JOIN musical_works AS m 
ON a.musical_work_id = m.id 
LEFT JOIN (
     SELECT sources.musical_work_id FROM sources 
     GROUP BY sources.musical_work_id 
     HAVING COUNT(sources.musical_work_id) <= 8) 
AS t 
ON m.id = t.musical_work_id 
WHERE 
m.genre='rock' AND 
t.musical_work_id IS NULL