mysql索引排序优化小记

系统开发中有如下SQL语句:

SELECT c.customer_id,
               c.customer_name,
               c.sex,
               c.tel,
               c.is_verification AS verificationFlg,
               c.salesman,
               c.customer_type,
               u.user_name       AS salesmanName,
               c_s.dict_content  as color,
               orders.cnt        as hasUncompleteOrder
        FROM
             (
               SELECT find_child_dept_ids('1,28') pids) p,
             customer c 
               LEFT OUTER JOIN t_user u
                               ON c.salesman = u.id
               LEFT OUTER JOIN mt_staff ms
                               ON ms.staff_id = u.staff_id
               LEFT OUTER JOIN mt_dict c_s
                               ON c_s.dict_id = c.customer_star AND c_s.is_deleted = 0 and c_s.dict_type_id = 'CR001'
               LEFT OUTER JOIN (
               SELECT count(order_id) AS cnt,customer_id
               FROM `order`
               WHERE del_flag = 0
                 and fee_status = 1
               GROUP BY customer_id
               HAVING cnt > 0) orders ON orders.customer_id = c.customer_id
        WHERE (c.is_deleted = 0 AND c.black_state = '0' AND (FIND_IN_SET(ms.dept_id, p.pids)))
        ORDER BY c.customer_id desc
        LIMIT 15;

数据库表customer记录数达到15万+,order数达到5万左右,此条SQL执行时间为8s+,这是客户端所不能接受的,而且我们排序的字段已经是主键了,主键自带索引,所以这个查询一定是有问题的。我们explain分析结果如下:

我们能看到分析结果中显示了filesort,mysql排序分为索引排序和文件排序,文件排序一定是慢的,当你的sql语句中出现这个,也就意味着需要优化。

然后问题就变成了如何让这条SQL语句执行索引排序?

查阅了一些资料,发现有的说法是where条件中的索引和排序索引不是同一字段导致的,我尝试了如下修改:

SELECT c.customer_id,
               c.customer_name,
               c.sex,
               c.tel,
               c.is_verification AS verificationFlg,
               c.salesman,
               c.customer_type,
               u.user_name       AS salesmanName,
               c_s.dict_content  as color,
               orders.cnt        as hasUncompleteOrder
        FROM
             (
               SELECT find_child_dept_ids('1,28') pids) p,
             customer c
               LEFT OUTER JOIN t_user u
                               ON c.salesman = u.id
               LEFT OUTER JOIN mt_staff ms
                               ON ms.staff_id = u.staff_id
               LEFT OUTER JOIN mt_dict c_s
                               ON c_s.dict_id = c.customer_star AND c_s.is_deleted = 0 and c_s.dict_type_id = 'CR001'
               LEFT OUTER JOIN (
               SELECT count(order_id) AS cnt,customer_id
               FROM `order`
               WHERE del_flag = 0
                 and fee_status = 1
               GROUP BY customer_id
               HAVING cnt > 0) orders ON orders.customer_id = c.customer_id
        WHERE (c.is_deleted = 0 AND c.black_state = '0' AND (FIND_IN_SET(ms.dept_id, p.pids)) and c.customer_id > 0)
        ORDER BY c.customer_id desc
        LIMIT 15;

但是explain出来还是filesort,这就让我很费解。现在问题就又变成了为什么会让我的索引排序变成文件排序呢?

经过排查发现是我在SQL中使用了临时表,并且在where条件中引用了临时表,所以查询优化器帮我们优化SQL时,就没有使用索引优化。

好,那我们再回来解决第一个问题,如何加快速度?

我们可以手动强制让查询优化器使用某个索引:

SELECT c.customer_id,
       c.customer_name,
       c.sex,
       c.tel,
       c.is_verification AS verificationFlg,
       c.salesman,
       c.customer_type,
       u.user_name       AS salesmanName,
       c_s.dict_content  as color,
       orders.cnt        as hasUncompleteOrder
FROM (
       SELECT find_child_dept_ids('1,28') pids) p,
     customer c use index (`PRIMARY`)
       LEFT OUTER JOIN t_user u
                       ON c.salesman = u.id
       LEFT OUTER JOIN mt_staff ms
                       ON ms.staff_id = u.staff_id
       LEFT OUTER JOIN mt_dict c_s
                       ON c_s.dict_id = c.customer_star AND c_s.is_deleted = 0 and c_s.dict_type_id = 'CR001'
       LEFT OUTER JOIN (
       SELECT count(order_id) AS cnt,customer_id
       FROM `order`
       WHERE del_flag = 0
         and fee_status = 1
       GROUP BY customer_id
       HAVING cnt > 0) orders ON orders.customer_id = c.customer_id
WHERE (c.is_deleted = 0 AND c.black_state = '0' AND (FIND_IN_SET(ms.dept_id, p.pids)) and c.customer_id > 0)
ORDER BY c.customer_id desc
LIMIT 15;

至此大功告成。

你可能感兴趣的:(SQL)