前兩天接觸了一個項目,裏面的一個報表查詢功能,訪問數據庫很慢,嚴重時請求直接超時,寫的sql語句得有三四百行,真是頭疼,沒辦法,硬着頭皮,只能一段一段的分析sql,查詢sql慢的問題。解決思路就是使用EXPLAIN 命令來查看哪些表沒有用到索引。通過查找終於找到問題所在,現記錄如下:
1、問題所在:
join on 條件後面不能使用or連接,造成索引失效,會全表掃描。
sql語句實例:
SELECT
sp.id,
gcm.id AS gcmId,
gcm.material_id
FROM
gt_supply_order sp
LEFT JOIN gt_supply_order_material gm ON sp.id = gm.supply_order_id
LEFT JOIN gt_contract gc ON sp.contract_id = gc.id
left join gt_orderform_goods nm on nm.order_id=gc.id or nm.id=gm.order_form_goods_id
LEFT JOIN gt_orderform_goods gcm ON gcm.id = nm.id
OR gcm.main_contract_goods_id = nm.id
AND gcm.deleted = 0
WHERE
sp.deleted = 0
AND gm.deleted = 0
AND sp.contract_id = #{jgMaterialSupplyVO.contractId} and sp.goods_class_name = #{jgMaterialSupplyVO.goodsName} and sp.supply_order_status_enum=4
GROUP BY
sp.id,gcm.id
2、解決方案:
上面查詢(join on or)可以用 union 替換(索引有效),缺點sql語句長(相比性能問題還是可取的)
中間有個小插曲:
union 操作會對結果去重且排序,union all 不會去重
優化後的SQL如下:
select a.id,gcm.id AS gcmId,gcm.material_id from(
SELECT
sp.id,
nm.id as nmid
FROM
gt_supply_order sp
LEFT JOIN gt_supply_order_material gm ON sp.id = gm.supply_order_id
LEFT JOIN gt_contract gc ON sp.contract_id = gc.id
left join gt_orderform_goods nm on nm.order_id=gc.id
WHERE
sp.deleted = 0
AND gm.deleted = 0 AND nm.deleted=0
AND sp.contract_id = 137957291194000 and sp.goods_class_name = '橋樑支座' and sp.supply_order_status_enum=4
union
SELECT
sp.id,
nm.id as nmid
FROM
gt_supply_order sp
LEFT JOIN gt_supply_order_material gm ON sp.id = gm.supply_order_id
LEFT JOIN gt_contract gc ON sp.contract_id = gc.id
left join gt_orderform_goods nm on nm.id=gm.order_form_goods_id
WHERE
sp.deleted = 0
AND gm.deleted = 0 AND nm.deleted=0
AND sp.contract_id = 137957291194000 and sp.goods_class_name = '橋樑支座' and sp.supply_order_status_enum=4
)a
LEFT JOIN gt_orderform_goods gcm ON gcm.id = a.nmid
where gcm.id IS NOT NULL
GROUP BY a.id,gcm.id
union
select b.id,gcm.id AS gcmId,gcm.material_id from
(
SELECT
sp.id,
nm.id as nmid
FROM
gt_supply_order sp
LEFT JOIN gt_supply_order_material gm ON sp.id = gm.supply_order_id
LEFT JOIN gt_contract gc ON sp.contract_id = gc.id
left join gt_orderform_goods nm on nm.order_id=gc.id
WHERE
sp.deleted = 0
AND gm.deleted = 0 AND nm.deleted=0
AND sp.contract_id = 137957291194000 and sp.goods_class_name = '橋樑支座' and sp.supply_order_status_enum=4
union
SELECT
sp.id,
nm.id as nmid
FROM
gt_supply_order sp
LEFT JOIN gt_supply_order_material gm ON sp.id = gm.supply_order_id
LEFT JOIN gt_contract gc ON sp.contract_id = gc.id
left join gt_orderform_goods nm on nm.id=gm.order_form_goods_id
WHERE
sp.deleted = 0
AND gm.deleted = 0 AND nm.deleted=0
AND sp.contract_id = 137957291194000 and sp.goods_class_name = '橋樑支座' and sp.supply_order_status_enum=4
)b
LEFT JOIN gt_orderform_goods gcm ON gcm.main_contract_goods_id = b.nmid
where gcm.id IS NOT NULL
GROUP BY
b.id,gcm.id
3、總結:
(1)通過上面的優化,查詢速度由原來的77秒,提速到了0.107秒,提高700多倍,可見,SQL優化是多麼的重要呀。
(2)這種優化方式可能不是最優的(sql語句太長),如有高人還有其他優化方式,希望可以交流,謝謝。
(3)有興趣的小夥伴可以測試下,歡迎交流。