回顧
上一篇了解了分析SQL使用的explain
,可以點擊查看MySQL篇-SQL優化實戰了解我在寫sql的注意事項還有explain
的說明,這次拿一段生產使用的sql
進行優化說明。從14s
優化到2.6s
待優化的SQL
SELECT DISTINCTswpe.tag_number,hca.ACCOUNT_NAME customer_name,sipa.PIN_LOGO area_number,cdla.delivery_header_id,swpe.pack_number,swph.packslip_number,cdpa.transport_mode,date_format(swpe.inware_date,'%Y-%m-%d %H:%i:%s') in_warehouse_date,DATE(cdla.act_delivery_date) act_delivery_date,cdla.plate_number,wbp.PLATFORM_NAME schedule_stage_mir,sooh.order_number,swph.lot_number,milk.ATTRIBUTE14,ifnull((SELECT'Y'FROMcwms_delivery_attachment_all cdaWHEREcda.pack_entity_id = swpe.pack_entity_idAND cda.stock_scan_status = 'Y'LIMIT 1),'N') stock_status,ifnull((SELECTcda.commentsFROMcwms_delivery_attachment_all cdaWHEREcda.pack_entity_id = swpe.pack_entity_idLIMIT 1),NULL) comments,ifnull((SELECTswdh.delivery_numberFROMsfy_wsh_delivery_lines_all swdlINNER JOIN sfy_wsh_delivery_headers_all swdh ON swdl.delivery_header_id = swdh.delivery_header_idWHEREswpe.tag_number = swdl.tag_numberLIMIT 1),NULL) delivery_number,ifnull((SELECTfilter1FROMeos_dict_entryWHEREDICTTYPEID = 'AUTH_CONFIG'LIMIT 1),'Y') zc_power,ppl.PICK_NUMBER pd_number,IF (ifnull((SELECTIF (substr(father.license_number, 1, 2) = 'TP',father.license_number,NULL) tp_numberFROMwms_mtl_onhand_quantities_detail childINNER JOIN wms_mtl_onhand_quantities_detail father ON child.parent_mq_id = father.mq_idWHEREchild.license_number = swpe.tag_numberLIMIT 1),1) != 1,(SELECTIF (substr(father.license_number, 1, 2) = 'TP',father.license_number,NULL) tp_numberFROMwms_mtl_onhand_quantities_detail childINNER JOIN wms_mtl_onhand_quantities_detail father ON child.parent_mq_id = father.mq_idWHEREchild.license_number = swpe.tag_numberLIMIT 1),(SELECTIF (substr(oldfather.license_number,1,2) = 'TP',oldfather.license_number,NULL) tp_numberFROMwms_mtl_onhand_quantities_detail childINNER JOIN wms_mtl_onhand_quantities_detail father ON child.parent_mq_id = father.mq_idINNER JOIN wms_mtl_onhand_quantities_detail oldfather ON father.parent_mq_id = oldfather.mq_idWHEREchild.license_number = swpe.tag_numberLIMIT 1)) tp_number,IF ((SELECTDOWNRACKSTYPEFROMwms_mtl_onhand_quantities_detailWHERElicense_number = swpe.tag_numberLIMIT 1) = 3,'Y','N') type,(SELECTTC_NUMBERFROMcwms_mobile_tray_lines cmtlWHEREcmtl.TAG_NUMBER = swpe.tag_numberORDER BYCREATION_DATE DESCLIMIT 1) tc_number,IF ((SELECTDOWNRACKSTYPEFROMwms_mtl_onhand_quantities_detailWHERElicense_number = swpe.tag_numberLIMIT 1) = 3,1,0) is_scan
FROMcwms_delivery_lines_all cdla
INNER JOIN hz_cust_accounts hca ON cdla.customer_id = hca.cust_account_id
INNER JOIN sfy_oe_order_headers_all sooh ON sooh.oe_header_id = cdla.oe_header_id
INNER JOIN sfy_wsh_pack_entities swpe ON cdla.oe_header_id = swpe.oe_header_id
INNER JOIN sfy_wsh_packslip_headers_all swph ON swpe.header_id = swph.header_id
INNER JOIN cwms_delivery_plan_all cdpa ON cdpa.DELIVERY_HEADER_ID = cdla.DELIVERY_HEADER_ID
LEFT JOIN mtl_secondary_inventories msit ON msit.organization_id = swpe.organization_id
AND msit.secondary_inventory_name = swpe.subinventory_code
LEFT JOIN pick_pack_link ppl ON ppl.TAG_NUMBER = swpe.TAG_NUMBER
LEFT JOIN mtl_item_locations_kfv milk ON swpe.LOCATOR_ID = milk.INVENTORY_LOCATION_ID
AND milk.ENABLE_FLAG = 1
LEFT JOIN sfy_inv_pd_agent sipa ON sipa.CUST_ACCOUNT_ID = hca.CUST_ACCOUNT_ID
AND sipa.ORGANIZATION_ID = swpe.ORGANIZATION_ID
LEFT JOIN (SELECTDELIVERY_HEADER_ID,SCHEDULE_STAGE,group_concat(CAR_NUMBER) CAR_NUMBER,group_concat(DISTINCT LOGISTIC_PROVIDER) LOGISTIC_PROVIDERFROMwms_delivery_car_detailGROUP BYDELIVERY_HEADER_ID
) wdcd ON cdpa.DELIVERY_HEADER_ID = wdcd.DELIVERY_HEADER_ID
LEFT JOIN wms_bill_platform wbp ON wbp.PLATFORM_CODE = wdcd.SCHEDULE_STAGE
AND wbp.PLATFORM_ENABLE_FLAG = 1
WHEREswpe.tag_number IS NOT NULL
AND swpe.pack_number IS NOT NULL
AND swpe. STATUS != 'X'
AND cdpa.approve_status = 'Y'
AND cdpa.inv_approve_status = 'Y'
AND hca.account_number = 'GPS21017802'
AND cdla.act_delivery_date = '2024-06-26'
AND ifnull(swpe.delivery_date,cdla.act_delivery_date
) >= cdla.act_delivery_date
ORDER BYmilk.CONCATENATED_SEGMENTS
1、問題展示
查詢效率:3w
條數據,耗費14s
2、問題排查
2.1、操作思路
通過執行計劃看到wms_delivery_car_detail
的執行計劃好像有優化空間,先將這張表的關聯移除后查看執行效率,以確認這張表影響的程度
2.2、執行結果
查詢效率13.9s
,發現移除后并沒有顯著的提高,說明這個子查詢的執行計劃并沒有 很大的效率問題
3、大膽假設,小心求證
3.1、操作思路
那么接下來看看其他執行計劃,發現而且在字段上的子查詢有很多,假如我們把所有字段中的子查詢都移除了會有什么效果——移除了執行計劃中select_type=DEPENDENT SUBQUERY
的子查詢后,只需要1.3s
就拿到查詢結果了,執行計劃如圖所示,由此可知字段上的子查詢多了,結果集大時會對查詢效率有很大的影響。
既然找到了問題出現在子查詢上,但這些字段還是要查的,只是我們得換種方式,目的是移除子查詢的情況下依然查詢所需字段,那就要修改為連接查詢的方式,如先把關聯相同表的多個子查詢通過表關聯的方式合并為一次關聯。
3.2、子查詢分析
子查詢表 | 對應的子查詢數量 | 代碼行數 |
---|---|---|
cwms_delivery_attachment_all | 2個 | 第21至28,34至40 |
wms_mtl_onhand_quantities_detail | 2個 | 第86至95,100至112,115至132,138至144 |
sfy_wsh_delivery_headers_all | 1個 | 第55至62 |
cwms_mobile_tray_lines | 1個 | 第150至158(因為其中的tag_number是唯一的,經業務確認此處的order by 可移除) |
3.3、執行結果
修改為連接查詢后,查詢耗時僅需要2.6s
,執行計劃如下:
-- 修改后的sql
SELECT DISTINCTswpe.tag_number,hca.ACCOUNT_NAME customer_name,sipa.PIN_LOGO area_number,cdla.delivery_header_id,swpe.pack_number,swph.packslip_number,cdpa.transport_mode,date_format(swpe.inware_date,'%Y-%m-%d %H:%i:%s') in_warehouse_date,DATE(cdla.act_delivery_date) act_delivery_date,wbp.PLATFORM_NAME schedule_stage_mir,sooh.order_number,swph.lot_number,milk.ATTRIBUTE14,ifnull(cdaa.stock_scan_status, 'N') stock_status,ifnull(cdaa.comments, NULL) comments,ifnull(swdh.delivery_number, NULL) delivery_number,ifnull((SELECTfilter1FROMeos_dict_entryWHEREDICTTYPEID = 'AUTH_CONFIG'LIMIT 1),'Y') zc_power,ppl.PICK_NUMBER pd_number,IF (-- wmoqd.TP_NUMBER是原關聯fater.license_number或者oldfather.license_number的值substr(wmoqd.TP_NUMBER, 1, 2) = 'TP',wmoqd.TP_NUMBER,NULL) tp_number,IF (wmoqd.DOWNRACKSTYPE = 3, 'Y', 'N') type,cmtl.TC_NUMBER tc_number,IF (wmoqd.DOWNRACKSTYPE = 3, 1, 0) is_scan
FROMcwms_delivery_lines_all cdla
INNER JOIN hz_cust_accounts hca ON cdla.customer_id = hca.cust_account_id
INNER JOIN sfy_oe_order_headers_all sooh ON sooh.oe_header_id = cdla.oe_header_id
INNER JOIN sfy_wsh_pack_entities swpe ON cdla.oe_header_id = swpe.oe_header_id
INNER JOIN sfy_wsh_packslip_headers_all swph ON swpe.header_id = swph.header_id
INNER JOIN cwms_delivery_plan_all cdpa ON cdpa.DELIVERY_HEADER_ID = cdla.DELIVERY_HEADER_ID
LEFT JOIN mtl_secondary_inventories msit ON msit.organization_id = swpe.organization_idAND msit.secondary_inventory_name = swpe.subinventory_code
LEFT JOIN pick_pack_link ppl ON ppl.TAG_NUMBER = swpe.TAG_NUMBER
LEFT JOIN mtl_item_locations_kfv milk ON swpe.LOCATOR_ID = milk.INVENTORY_LOCATION_IDAND milk.ENABLE_FLAG = 1
LEFT JOIN sfy_inv_pd_agent sipa ON sipa.CUST_ACCOUNT_ID = hca.CUST_ACCOUNT_IDAND sipa.ORGANIZATION_ID = swpe.ORGANIZATION_ID
LEFT JOIN (SELECTDELIVERY_HEADER_ID,SCHEDULE_STAGE,group_concat(CAR_NUMBER) CAR_NUMBER,group_concat(DISTINCT LOGISTIC_PROVIDER) LOGISTIC_PROVIDERFROMwms_delivery_car_detailGROUP BYDELIVERY_HEADER_ID
) wdcd ON cdpa.DELIVERY_HEADER_ID = wdcd.DELIVERY_HEADER_ID
LEFT JOIN wms_bill_platform wbp ON wbp.PLATFORM_CODE = wdcd.SCHEDULE_STAGEAND wbp.PLATFORM_ENABLE_FLAG = 1
-- 主要改動在這里:從子查詢遷移到下面左關聯
LEFT JOIN cwms_delivery_attachment_all cdaa ON cdaa.PACK_ENTITY_ID=swpe.PACK_ENTITY_ID
LEFT JOIN wms_mtl_onhand_quantities_detail wmoqd ON wmoqd.LICENSE_NUMBER=swpe.TAG_NUMBER AND wmoqd.ORGANIZATION_ID=swpe.ORGANIZATION_ID
LEFT JOIN sfy_wsh_delivery_lines_all swdl ON swdl.TAG_NUMBER=swpe.TAG_NUMBER
LEFT JOIN sfy_wsh_delivery_headers_all swdh ON swdh.DELIVERY_HEADER_ID=swdl.DELIVERY_HEADER_ID
LEFT JOIN cwms_mobile_tray_lines cmtl ON cmtl.TAG_NUMBER=swpe.TAG_NUMBER
WHEREswpe.tag_number IS NOT NULL
AND swpe.pack_number IS NOT NULL
AND swpe. STATUS != 'X'
AND cdpa.approve_status = 'Y'
AND cdpa.inv_approve_status = 'Y'
AND hca.account_number = 'GPS21017802'
AND cdla.act_delivery_date = '2024-06-26'
AND ifnull(swpe.delivery_date,cdla.act_delivery_date
) >= cdla.act_delivery_date
GROUP BY swpe.TAG_NUMBER
ORDER BYmilk.CONCATENATED_SEGMENTS
總結
- 當結果集字段中有好幾個相同表的子查詢時,將子查詢修改為連接查詢的效率提升會比較大(相當于一行記錄處理一次查詢)
- 有時候執行計劃可能無法直接看出修改哪里能提升,但能給我們提供優化的思路
- 在執行計劃中看到每個表都走索引了,但是卻還是很慢,那我們可以一段一段的、一表一表的排除,找到問題點在哪,而多快能找到主要就取決于經驗還有對表的熟悉程度了。