系统开发中有如下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;
至此大功告成。