2013-05-06 2 views
0

J'essaie d'optimiser la requête la plus longue que j'ai jamais écrite en utilisant le MySQL EXPLAIN mais puisque c'est mon premier je n'arrive pas à comprendre le résultat. Voici la requête et le résultat de l'exécution d'une commande EXPLAIN:Optimisation d'une requête complexe

EXPLAIN SELECT pb.name, s1.MessageFrom, s1.MessageText, s1.SendTime, s1.is_unread, s1.Id, s1.autoreply_sent FROM sol_inbound s1 
    JOIN sol_contactnum c ON s1.MessageFrom = c.number 
    JOIN sol_phonebk_contactnum USING (contactnum_id) 
    JOIN sol_phonebk pb USING (phonebk_id) 
    JOIN sol_message_folder mf ON s1.Id = mf.message_id 
    WHERE (MessageFrom, SendTime) IN (SELECT MessageFrom, MAX(SendTime) FROM sol_inbound inb 
     JOIN sol_message_folder mf WHERE inb.Id = mf.message_id 
     AND mf.folder_id=1 AND mf.direction='inbound' AND mf.user_id=1 
     GROUP BY MessageFrom) 
    AND mf.folder_id=1 AND mf.direction='inbound' AND mf.user_id=1 
    UNION 
    SELECT NULL `name`, s1.MessageFrom, s1.MessageText, s1.SendTime, s1.is_unread, s1.Id, s1.autoreply_sent FROM sol_inbound s1 
    LEFT JOIN sol_contactnum c ON s1.MessageFrom = c.number 
    JOIN sol_message_folder mf ON s1.Id = mf.message_id 
    WHERE c.number IS NULL 
    AND mf.folder_id=1 AND mf.direction='inbound' AND mf.user_id=1 
    AND (MessageFrom, SendTime) IN (SELECT MessageFrom, MAX(SendTime) FROM sol_inbound inb 
     JOIN sol_message_folder mf WHERE inb.Id = mf.message_id 
     AND mf.folder_id=1 AND mf.direction='inbound' AND mf.user_id=1 
     GROUP BY MessageFrom) 
    ORDER BY SendTime DESC LIMIT 100 

Les EXPLAIN résultats dans:

id select_type   table     type possible_keys             key    key_len ref              rows Extra     
------ ------------------ ---------------------- ------ ------------------------------------------------------------- ---------------- ------- ---------------------------------------------------- ------ ------------------------ 
    1 PRIMARY    pb      ALL  PRIMARY              (NULL)   (NULL) (NULL)             303       
    1 PRIMARY    sol_phonebk_contactnum ref  PRIMARY,phonebk_id1_idx,contactnum_id1_idx,phonebk_contactnum PRIMARY   4  googlep1_solane.pb.phonebk_id        1 Using index    
    1 PRIMARY    c      eq_ref PRIMARY,number_idx            PRIMARY   4  googlep1_solane.sol_phonebk_contactnum.contactnum_id  1       
    1 PRIMARY    s1      ref  PRIMARY,message_from_idx          message_from_idx 243  googlep1_solane.c.number         1 Using where    
    1 PRIMARY    mf      eq_ref PRIMARY              PRIMARY   22  const,googlep1_solane.s1.Id,const,const     1 Using where; Using index 
    2 DEPENDENT SUBQUERY inb      index PRIMARY              message_from_idx 243  (NULL)              1       
    2 DEPENDENT SUBQUERY mf      eq_ref PRIMARY              PRIMARY   22  const,googlep1_solane.inb.Id,const,const     1 Using where; Using index 
    3 UNION    s1      ALL  PRIMARY              (NULL)   (NULL) (NULL)             877 Using where    
    3 UNION    c      ref  number_idx              number_idx  243  googlep1_solane.s1.MessageFrom        1 Using where; Using index 
    3 UNION    mf      eq_ref PRIMARY              PRIMARY   22  const,googlep1_solane.s1.Id,const,const     1 Using where; Using index 
    4 DEPENDENT SUBQUERY inb      index PRIMARY              message_from_idx 243  (NULL)              1       
    4 DEPENDENT SUBQUERY mf      eq_ref PRIMARY              PRIMARY   22  const,googlep1_solane.inb.Id,const,const     1 Using where; Using index 
(NULL) UNION RESULT  <union1,3>    ALL  (NULL)               (NULL)   (NULL) (NULL)            (NULL) Using filesort   

Le UNION au milieu de la requête rejoint ceux qui apparaissent les numéros de qui dans le répertoire avec ceux qui ne pas (ainsi le LEFT JOIN).

Edit:

Qu'est-ce que cette requête n'est obtenir le plus le dernier message entrant pour chaque numéro et le retourne. Je peux utiliser GROUP BY car il renvoie le message le plus ancien ... J'ai besoin de la dernière. Ensuite, il la rejoint ces chiffres qui n'existent pas dans le répertoire qui est la raison pour laquelle je vérifie pour WHERE c.number IS NULL.

+0

Quelle version de MySQL? De http://dev.mysql.com/doc/refman/5.7/en/using-explain.html: "Lorsque vous précedez une instruction avec le mot-clé EXPLAIN, MySQL affiche des informations de l'optimiseur sur le plan d'exécution de la requête. , MySQL explique comment il traiterait l'instruction, y compris des informations sur la manière dont les tables sont jointes et dans quel ordre. " – MichaelJCox

+0

Version 5.0.10 Je suppose. Quelle que soit la version livrée avec le dernier XAMPP. – enchance

+1

Basé sur le plan expliquer, je suppose que cette requête est assez rapide. Cela vous donne-t-il des problèmes? Je suppose que je ne suis pas tout à fait sûr de la question. –

Répondre

0

Si possible, utiliser un sous-requête décorrélé comme suit ...

FROM... x 
JOIN 
    (SELECT MessageFrom 
      , MAX(SendTime) max_sendtime 
     FROM sol_inbound inb 
     JOIN sol_message_folder mf 
     ON inb.Id = mf.message_id 
     WHERE mf.folder_id=1 
     AND mf.direction='inbound' 
     AND mf.user_id=1 
     GROUP 
     BY MessageFrom 
    ) y 
    ON y.messagefrom = x.messagefrom 
AND y.max_sendtime = x.sendtime 
0

Il semble que vous avez utilisé 2 sub-query's pour filtrer les résultats, ceux-ci ne sont pas vraiment nécessaires. Je suppose que vous devez afficher le latest message de chaque ID MessageFrom.

Essayez ceci pour des résultats plus rapides

SELECT 
    * 
FROM 
    (SELECT 
    pb.name, 
    s1.MessageFrom, 
    s1.MessageText, 
    s1.SendTime, 
    s1.is_unread, 
    s1.Id, 
    s1.autoreply_sent, 
    @row_num := IF(@prev_value=s1.MessageFrom,@row_num+1,1) AS row_num, 
    @prev_value := s1.MessageFrom 
    FROM 
    sol_inbound s1 
    JOIN sol_contactnum c ON s1.MessageFrom = c.number 
    JOIN sol_phonebk_contactnum USING (contactnum_id) 
    JOIN sol_phonebk pb USING (phonebk_id) 
    JOIN sol_message_folder mf ON s1.Id = mf.message_id 
    WHERE 
     mf.folder_id=1 
     AND mf.direction='inbound' 
     AND mf.user_id=1 
    ORDER BY 
    s1.MessageFrom, 
    s1.sendTime desc) temp WHERE temp.row_num = 1 

UNION 

SELECT 
    * 
FROM 
    (  
    SELECT 
     NULL `name`, 
     s1.MessageFrom, 
     s1.MessageText, 
     s1.SendTime, 
     s1.is_unread, 
     s1.Id, 
     s1.autoreply_sent, 
     @row_num := IF(@prev_value=s1.MessageFrom,@row_num+1,1) AS row_num, 
     @prev_value := s1.MessageFrom 
    FROM 
     sol_inbound s1 
     LEFT JOIN sol_contactnum c ON s1.MessageFrom = c.number 
     JOIN sol_message_folder mf ON s1.Id = mf.message_id 
    WHERE 
     c.number IS NULL 
     AND mf.folder_id=1 
     AND mf.direction='inbound' 
     AND mf.user_id=1 
    ORDER BY 
     s1.MessageFrom, 
     s1.sendTime desc  
     ) temp2 WHERE temp2.row_num = 1 

ORDER BY 
    SendTime DESC 
LIMIT 100 

Au lieu d'utiliser un sub-query pour filtrer les résultats, je l'ai utilisé session vars Mysql pour maintenir le rang de tous les messages d'un MessageFrom. Celui ayant le latest sendtime a le rank comme 1