SQL查询耗时超4分钟,寻求高效优化方案
咱来搞定这个慢查询,从索引、逻辑和执行计划这几个核心方向给你捋优化方案:
一、优先添加针对性索引(最核心的优化点)
你的几个表数据量不小,尤其是ps_imp_combinaison_parametre_value_6有70多万行,全表扫描肯定慢,给关联、过滤、排序的字段加联合索引,能直接把查询速度提上来:
- 给
ps_imp_combinaison_parametre_value_6加联合索引(关联字段优先):
CREATE INDEX idx_cpv_idcomb_idpv ON ps_imp_combinaison_parametre_value_6 (id_combinaison, id_parametre_value);
- 给
ps_imp_combinaison_6加联合索引(过滤+关联+排序字段):
CREATE INDEX idx_ic_idprod_idcomb ON ps_imp_combinaison_6 (id_product_impression, id_combinaison);
- 给
ps_imp_parametre_value加联合索引(关联+过滤字段):
CREATE INDEX idx_pv_idparam_idpv ON ps_imp_parametre_value (id_parametre, id_parametre_value);
- 给
ps_imp_parametre加联合索引(过滤+关联字段):
CREATE INDEX idx_p_idnom_idparam ON ps_imp_parametre (id_nom_domaine, id_parametre);
- 给
ps_imp_parametre_value_lang加联合索引(过滤+关联字段):
CREATE INDEX idx_pvl_idlang_idpv ON ps_imp_parametre_value_lang (id_lang_domaine, id_parametre_value);
- 给
ps_imp_product_impression_parametre_value加联合索引(过滤+关联字段):
CREATE INDEX idx_pipv_idprod_idpv ON ps_imp_product_impression_parametre_value (id_product_impression, id_parametre_value);
二、简化查询逻辑,减少无效扫描
看你的原查询,有些JOIN可以优化,避免不必要的行数扫描:
- 原查询里
LEFT JOIN ps_imp_parametre p,但后面WHERE条件有p.id_nom_domaine = 6,这会把LEFT JOIN自动转换成INNER JOIN(因为NULL值不满足=6),直接改成INNER JOIN能提前过滤掉无效数据:
把LEFT JOIN ps_imp_parametre p ON p.id_parametre = pv.id_parametre改成INNER JOIN ps_imp_parametre p ON p.id_parametre = pv.id_parametre - 如果业务上
ps_imp_product_impression_parametre_value的记录是必须存在的,也可以把对应的LEFT JOIN改成INNER JOIN,进一步缩小结果集。
另外,你的ORDER BY用到了p.id_parametre,但GROUP BY里没包含它,虽然MySQL某些模式下允许,但建议把p.id_parametre加到GROUP BY里(或者确认它和GROUP BY的字段是函数依赖关系),这样既符合SQL标准,也能让排序更高效。
三、优化GROUP BY与ORDER BY的执行效率
你的GROUP BY是ic.id_combinaison, cpv.id_parametre_value,ORDER BY是ic.id_combinaison, p.id_parametre,刚才加的索引已经覆盖了这些字段的顺序,MySQL可以直接利用索引的排序结果,避免创建临时表和文件排序,这能大幅提升这一步的速度。
另外,虽然你用了LIMIT 0,50,但如果前面的分组和排序逻辑低效,还是会慢,所以前面的索引优化是基础。
四、用EXPLAIN验证优化效果
执行EXPLAIN加上你的查询语句,比如:
EXPLAIN SELECT pvl.name_parametre_value_parametre_value_lang, pv.id_parametre_value, ic.id_combinaison, ic.prix_combinaison, ic.poid_combinaison, ic.actif_combinaison, ic.actif_genere, pv.actif_value, pipv.id_parametre_value as pipv_id_parametre_value FROM ps_imp_combinaison_parametre_value_6 cpv LEFT JOIN ps_imp_combinaison_6 ic ON ic.id_combinaison = cpv.id_combinaison LEFT JOIN ps_imp_parametre_value pv ON pv.id_parametre_value = cpv.id_parametre_value LEFT JOIN ps_imp_parametre_value_lang pvl ON pvl.id_parametre_value = pv.id_parametre_value INNER JOIN ps_imp_parametre p ON p.id_parametre = pv.id_parametre LEFT JOIN ps_imp_product_impression_parametre_value pipv ON pipv.id_parametre_value = pv.id_parametre_value and pipv.id_product_impression = 63 WHERE p.id_nom_domaine = 6 AND pvl.id_lang_domaine = 18 AND ic.id_product_impression = 63 GROUP BY ic.id_combinaison, cpv.id_parametre_value, p.id_parametre ORDER BY ic.id_combinaison, p.id_parametre LIMIT 0, 50;
看输出的type列,尽量避免ALL(全表扫描);key列要能看到我们刚才创建的索引;rows列的数值要尽量小,说明扫描的行数少。如果某个表还是全表扫描,检查索引的字段顺序是否和查询条件匹配,有没有隐式类型转换的问题。
内容的提问来源于stack exchange,提问作者user9123285
相关产品推荐
相关产品推荐

