MySQL 通过连接、分组依据、计数排序进行优化

MySQL optimization with joins, group by, order by count

以下查询的执行时间高达 4 秒,我似乎无法弄清楚原因。我有一个关于客户的索引(已保存、已删除、已创建,rep_id),它消除了完整的 table 扫描,但它对加快速度没有太大作用...

SELECT 
customer.rep_id AS `ID`,
COUNT(*) AS Count,
rep.name
FROM customer
INNER JOIN appointment ON customer.id = appointment.customer_id
  AND appointment.date >= '2017-05-01'
  AND appointment.date < '2017-06-01'
  AND appointment.current = 1
  AND appointment.`status` = 'completed'
INNER JOIN rep ON customer.rep_id = rep.user_id
INNER JOIN user ON rep.user_id = user.id
  AND user.active = 1
  AND user.deleted = 0
WHERE customer.rep_id != 0
AND customer.saved = 0
AND customer.deleted = 0
GROUP BY customer.rep_id 
ORDER BY `Count` DESC
LIMIT 50

解释输出:

id  1
select_type SIMPLE
table   customer
type    ref
possible_keys   PRIMARY,rep_id,saved_deleted_created_rep,rep_saved_deleted_created
key saved_deleted_created_rep
key_len 2
ref const,const
rows    162007
Extra   Using where; Using index; Using temporary; Using filesort

id  1
select_type SIMPLE
table   contact
type    ref
possible_keys   user_id
key user_id
key_len 4
ref customer.rep_id
rows    1
Extra   Using index condition

id  1
select_type SIMPLE
table   user
type    eq_ref
possible_keys   PRIMARY
key PRIMARY
key_len 4
ref contact.user_id
rows    1
Extra   Using where

id  1
select_type SIMPLE
table   appointment
type    ref
possible_keys   status,date,customer_id
key customer_id
key_len 4
ref customer.id
rows    1
Extra   Using where

在将它们全部合并之前,您是否尝试缩小 table 范围?

SELECT 
    customer.rep_id AS `ID`,
    COUNT(*) AS Count,
    contact.name
FROM 
    (
        SELECT 
            id, rep_id
        FROM 
            customer
            JOIN (
                SELECT 
                    customer_id 
                FROM 
                    appointment
                WHERE 
                    date >= '2017-05-01'
                    AND 
                    appointment.date < '2017-06-01'
                    AND 
                    appointment.current = 1
                    AND 
                    appointment.`status` = 'completed'
            ) AS appointment 
            ON customer.id = appointment.customer_id
        WHERE
            customer.rep_id != 0
            AND 
            customer.saved = 0
            AND 
            customer.deleted = 0
    ) AS customer
    JOIN contact 
        ON customer.rep_id = contact.user_id
    JOIN (
        SELECT
            id
        FROM
            user
        WHERE
            user.active = 1
            AND 
            user.deleted = 0
    ) AS user 
        ON contact.user_id = user.id        
GROUP BY customer.rep_id 
ORDER BY `Count` DESC
LIMIT 50

您可以更好地优化索引的另一种选择。让您的原始查询保持不变,只需对我进行格式化即可显示关键组件以帮助找出索引...

SELECT 
      customer.rep_id AS `ID`,
      COUNT(*) AS Count,
      rep.name
   FROM 
      customer
         INNER JOIN appointment 
            ON customer.id = appointment.customer_id
           AND appointment.date >= '2017-05-01'
           AND appointment.date < '2017-06-01'
           AND appointment.current = 1
           AND appointment.`status` = 'completed'
        INNER JOIN rep 
            ON customer.rep_id = rep.user_id
            INNER JOIN user 
               ON rep.user_id = user.id
              AND user.active = 1
              AND user.deleted = 0
   WHERE 
          customer.rep_id != 0
      AND customer.saved = 0
      AND customer.deleted = 0
   GROUP BY 
      customer.rep_id 
   ORDER BY 
      `Count` DESC
   LIMIT 
      50

帮助您查询的索引...不仅是客户 table,还有约会、代表和用户 table...

table        index
customer     ( saved, deleted, rep_id, id, created )  although created does not appear required
appointment  ( customer_id, current, `status`, date )
rep          (  user_id, name )
user         (  id, active, deleted )

其中一些索引覆盖了不需要返回到 原始数据页,因为所有关键元素都是索引的一部分。