优化复杂查询

时间:2013-05-06 15:11:37

标签: mysql explain

我正在尝试优化使用MySQL EXPLAIN编写的最长查询,但由于这是我的第一个,我似乎无法理解结果。以下是运行EXPLAIN命令的查询和结果:

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

EXPLAIN导致:

 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          

查询中间的UNION会将电话簿中出现的号码与不存在号码的人(LEFT JOIN)联系起来。

修改

此查询的作用是获取每个数字的最新入站消息并将其返回。我可以使用GROUP BY,因为它返回最旧的消息...我需要最新消息。然后它加入电话簿中不存在的那些数字,这就是我检查WHERE c.number IS NULL.的原因

2 个答案:

答案 0 :(得分:0)

如果可能,请使用不相关的子查询,如下所示......

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

答案 1 :(得分:0)

您似乎已使用2 sub-query's来过滤结果,这些并非真正需要。我假设您需要显示每个latest message ID中的MessageFrom

尝试此操作以获得更快的结果

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

我没有使用sub-query来过滤结果,而是在Mysql中使用session vars来保持MessageFrom的所有消息的排名。拥有latest sendtime的人rank1