2009-02-26 29 views
4

我正在使用通常在一秒內執行的查詢,但有時需要10到40秒才能完成。我其實並不完全清楚子查詢是如何工作的,我只知道它的工作原理,因爲它爲每個faverprofileid提供了15行。提高這個緩慢的MySQL查詢的提示?

我記錄緩慢的查詢,它告訴我5823244行被檢查,這很奇怪,因爲沒有任何地方接近任何涉及的表中的許多行(收藏夾表最多50,000行) 。

任何人都可以給我一些指針?這是子查詢的問題,需要使用filesort嗎?

編輯:運行解釋顯示用戶表不使用索引(即使id是主鍵)。額外說:使用臨時;使用filesort。

SELECT F.id,F.created,U.username,U.fullname,U.id,I.* 
FROM favorites AS F 
INNER JOIN users AS U ON F.faver_profile_id = U.id 
INNER JOIN items AS I ON F.notice_id = I.id 
WHERE faver_profile_id IN (360,379,95,315,278,1) 
AND F.removed = 0 
AND I.removed = 0 
AND F.collection_id is null 
AND I.nudity = 0 
AND (SELECT COUNT(*) FROM favorites WHERE faver_profile_id = F.faver_profile_id 
AND created > F.created AND removed = 0 AND collection_id is null) < 15 
ORDER BY F.faver_profile_id, F.created DESC; 
+0

如果您在小寫字母中寫入「select」,「from」,「where」,「和」,「in」和「order by」關鍵字以及「inner」,「join」 ,「on」和「desc」只有第一個大寫字母。 – 2009-02-26 08:55:17

+0

您是否嘗試爲創建的每個faver_profile_id排序選擇前15項? – Quassnoi 2009-02-26 09:01:11

+0

你在桌子上運行ANALYZE嗎? – vladr 2009-02-26 15:31:37

回答

5

我覺得跟GROUP BYHAVING它應該會更快。 這就是你想要的嗎?

SELECT F.id,F.created,U.username,U.fullname,U.id, I.field1, I.field2, count(*) as CNT 
FROM favorites AS F 
INNER JOIN users AS U ON F.faver_profile_id = U.id 
INNER JOIN items AS I ON F.notice_id = I.id 
WHERE faver_profile_id IN (360,379,95,315,278,1) 
AND F.removed = 0 
AND I.removed = 0 
AND F.collection_id is null 
AND I.nudity = 0 
GROUP BY F.id,F.created,U.username,U.fullname,U.id,I.field1, I.field2 
HAVING CNT < 15 
ORDER BY F.faver_profile_id, F.created DESC; 

不知道從items需要領域,所以我已經把佔位符。

4

檢查的行數很大,因爲很多行都被檢查過多次。 由於不正確的優化查詢計劃導致表掃描應該在執行索引查找時得到此結果。在這種情況下,檢查的行數是指數的,即與一個以上表中的行總數的乘積相當的數量級。

  • 請確保您有運行ANALYZE TABLE你的三個表。
  • 閱讀how to avoid table scans,並確定再創建任何缺失索引
  • 重新運行分析和重新解釋你的查詢
    • 檢查的行的數量必須大幅下降
    • 如果不是,發佈完整的解釋計劃
  • 使用query hints強制使用指數(見目錄名稱爲表,使用SHOW INDEX):

SELECT F.id,F.created,U.username,U.fullname,U.id,I.*
FROM favorites AS F FORCE INDEX (faver_profile_id_key)
INNER JOIN users AS UFORCE INDEX FOR JOIN (PRIMARY)ON F.faver_profile_id = U.id
INNER JOIN items AS I FORCE INDEX FOR JOIN (PRIMARY)ON F.notice_id = I.id
WHERE faver_profile_id IN (360,379,95,315,278,1)
AND F.removed = 0
AND I.removed = 0
AND F.collection_id is null
AND I.nudity = 0
AND (SELECT COUNT(*) FROM favoritesFORCE INDEX (faver_profile_id_key)WHERE faver_profile_id = F.faver_profile_id
AND created > F.created AND removed = 0 AND collection_id is null) < 15
ORDER BY F.faver_profile_id, F.created DESC;

你也可以改變你的查詢中使用GROUP BY faver_profile_id/HAVING count > 15代替嵌套SELECT COUNT(*)子查詢,通過vartec的建議。如果兩者都經過適當優化,例如原始和vartec的查詢的性能應該可比。使用提示(您的查詢就會使用嵌套的索引查找,而vartec的查詢將使用基於散列的策略。)

3

我建議你使用Mysql Explain Query來查看你的mysql服務器如何處理查詢。我的賭注是你的指標不是最優的,但解釋應該比我的賭注做得更好。

0

你可以做的每個ID和使用的限制,而不是COUNT(*)子查詢一個循環:

foreach $id in [123,456,789]: 
    SELECT 
    F.id, 
    F.created, 
    U.username, 
    U.fullname, 
    U.id, 
    I.* 
    FROM 
    favorites AS F INNER JOIN 
    users AS U ON F.faver_profile_id = U.id INNER JOIN 
    items AS I ON F.notice_id = I.id 
    WHERE 
    F.faver_profile_id = {$id} AND 
    I.removed = 0 AND 
    I.nudity = 0 AND 
    F.removed = 0 AND 
    F.collection_id is null 
    ORDER BY 
    F.faver_profile_id, 
    F.created DESC 
    LIMIT 
    15; 
0

我會假設查詢的結果intented被顯示爲分頁列表。在這種情況下,或許你可以考慮做一個更簡單的「未加入的查詢」,並對每行進行第二次查詢,以只讀取顯示的15,20或30個元素。不是一個繁重的操作?這將簡化查詢,並且在連接的表增長時它不會變慢。

請告訴我,如果我錯了,請。