2010-09-14 81 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