2014-03-06 58 views
0

我有這個疑問:MySQL是不使用prmary指數

SELECT SQL_NO_CACHE 
    COUNT(*) AS `numrows` 
FROM 
    (`citations`) 
     LEFT JOIN 
    `projects` ON `projects`.`project_id` = `citations`.`project_id` 
     LEFT JOIN 
    `users` ON `users`.`user_id` = `projects`.`user_id` 
WHERE 
    `users`.`role` = '0' 
     AND `citations`.`created` BETWEEN 1360213200 AND 1360299599 
     AND `citations`.`in_card` = '0' 
     AND `citations`.`citation_id` NOT IN (SELECT 
      user_stats_citations.citation_id 
     FROM 
      user_stats_citations, 
      user_stats FORCE INDEX (user_stats_type_index) 
     WHERE 
      user_stats_citations.user_stat_id = user_stats.id 
       AND user_stats.type IN (69 , 70, 71, 75, 76)); 

我有用戶表的索引:

users   0 PRIMARY        1 user_id  A    42836 (NULL) (NULL)   BTREE        
users   1 users_industry_id_index    1 industry_id A     118 (NULL) (NULL) YES  BTREE        
users   1 users_sponsor_index     1 sponsor  A     12 (NULL) (NULL) YES  BTREE 

這是EXPLAIN EXTENDED輸出

id select_type table type possible_keys key key_len ref rows filtered Extra 

    1 PRIMARY users ALL PRIMARY \N \N \N 42836 100.00 Using where 
    1 PRIMARY projects ref PRIMARY\,projects_user_id_index projects_user_id_index 4 citelighter.users.user_id 1 100.00 Using where; Using index 
    1 PRIMARY citations ref citations_project_id_index citations_project_id_index 4 citelighter.projects.project_id 4 100.00 Using index condition; Using where 
    2 SUBQUERY user_stats range user_stats_type_index user_stats_type_index 2 \N 410768 100.00 Using where; Using index 
    2 SUBQUERY user_stats_citations ref user_stats_citations_index_user_stat_id\,user_stats_citations_index_citation_id user_stats_citations_index_user_stat_id 8 citelighter.user_stats.id 1 100.00 \N 

我試圖在用戶LEFT JOIN上添加FORCE INDEX,但沒有使用索引。你能幫我解決這個問題嗎?因爲這個查詢在本地需要10秒鐘,生產環境需要1秒鐘。

+0

FWIW,我喜歡用\ G來解釋而不是; – Strawberry

+0

此外,「WHERE'用戶'.'role' ='0'」= INNER JOIN – Strawberry

回答

1

我注意到的第一件事是,這個謂詞在where子句中:WHERE users.role = '0'將您LEFT JOIN s到INNER JOIN S,所以你可能也只是讓他們內部連接。其次,MySQL在優化相關子查詢時存在問題,並且可能對派生表執行效果不佳。例如在this simple query

SELECT * 
FROM (SELECT * FROM T) T 
JOIN (SELECT * FROM T) T2 ON T.ID = T2.ID; 

即使ID是T主鍵,主鍵不用於聯接,因爲它不能被級聯出派生表的。同樣,有時當你寫:

SELECT * 
FROM T 
WHERE NOT EXISTS (SELECT 1 
        FROM T T2 
        WHERE T.Afield = T2.Afield 
        AND T2.AnotherField = 1); 

,子查詢的每一行外執行:

SELECT * 
FROM T 
WHERE Afield NOT IN (SELECT Afield FROM T WHERE AnotherField = 1); 

的MySQL並不一定兌現子查詢和使用,因爲它往往會重寫查詢查詢,因此如果外部查詢中有大量行,則對每行執行子查詢會變得非常昂貴。解決方案是儘可能避免子查詢。你的情況,你可以重寫查詢爲:

SELECT SQL_NO_CACHE 
     COUNT(*) AS `numrows` 
FROM `citations` 
     INNER JOIN `projects` 
      ON `projects`.`project_id` = `citations`.`project_id` 
     INNER JOIN `users` 
      ON `users`.`user_id` = `projects`.`user_id` 
     LEFT JOIN (user_stats_citations 
      INNER JOIN user_stats 
       ON user_stats_citations.user_stat_id = user_stats.id 
       AND user_stats.type IN (69 , 70, 71, 75, 76)) 
      ON user_stats_citations.citation_id = `citations`.`citation_id` 
WHERE `users`.`role` = '0' 
AND  `citations`.`created` BETWEEN 1360213200 AND 1360299599 
AND  `citations`.`in_card` = '0' 
AND  user_stats_citations.citation_id IS NULL; 

由於沒有子查詢沒有派生表或子查詢的行執行排。這應該會縮短執行時間。

0

這會給你什麼?

SELECT COUNT(*) numrows 
    FROM citations c 
    JOIN projects p 
    ON p.project_id = c.project_id 
    JOIN users u 
    ON u.user_id = p.user_id 

    LEFT 
    JOIN 
    (SELECT uc.citation_id 
     FROM user_stats_citations uc 
     JOIN user_stats us 
      ON uc.user_stat_id = us.id 
      AND us.type IN (69,70,71,75,76) 
    ) x 
    ON x.citation_id = c.citation_id 
WHERE u.role = 0 
    AND c.created BETWEEN 1360213200 AND 1360299599 
    AND c.in_card = 0 
    AND x.citation_id IS NULL 
+0

此解決方案在生產環境中需要2秒鐘。它也會返回0個numrows(正確的結果是209) – Urmelinho

+0

糟糕 - 小錯誤... – Strawberry

+0

我用'COUNT(*)'代替COUNT(1)''它會加快速度,因爲它沒有從表 – Chococroc