SQL優化——SQL語句拆分
阿新 • • 發佈:2020-12-07
曾經我也感覺我不會寫出執行耗時特別長的SQL,直到前幾天......
1、原SQL
這個SQL實際上的需求就是:根據“條件”去給done_status欄位賦值,但是這個條件太複雜了。
我們看到,大的方面,就是多個case(order_status取值0-11),但是有的在case的裡面進行了巢狀,最深的時候嵌套了5層case。這也是執行特別耗時的原因所在。
update super4s_order.base_order bo, super4s_order.procurement_order po, super4s_finance.finance_order fo set bo.done_status = ( case when po.order_status = 0 then 'PROCUREMENT_ORDER_CREATED' when po.order_status = 1 then 'PROCUREMENT_ORDER_CREATED,PROCUREMENT_WAIT_APPROVE' when po.order_status in (2,3) then 'PROCUREMENT_ORDER_CREATED,PROCUREMENT_WAIT_APPROVE,PROCUREMENT_TRANSACTION_CLOSE' when po.order_status in (11,5,10) then case when po.purchase_type = 2 then case when po.apply_pass_time is null then 'PROCUREMENT_ORDER_CREATED,PROCUREMENT_WAITING_FOR_DELIVERY' else 'PROCUREMENT_ORDER_CREATED,PROCUREMENT_WAIT_APPROVE,PROCUREMENT_WAITING_FOR_DELIVERY' end else case when po.apply_pass_time is null then 'PROCUREMENT_ORDER_CREATED' else 'PROCUREMENT_ORDER_CREATED,PROCUREMENT_WAIT_APPROVE' end end when po.order_status in (6,9) then case when po.purchase_type = 2 then case when po.apply_pass_time is null then 'PROCUREMENT_ORDER_CREATED' else 'PROCUREMENT_ORDER_CREATED,PROCUREMENT_WAIT_APPROVE' end else case when po.apply_pass_time is null then 'PROCUREMENT_ORDER_CREATED,PROCUREMENT_PENDING_PAYMENT' else 'PROCUREMENT_ORDER_CREATED,PROCUREMENT_WAIT_APPROVE,PROCUREMENT_PENDING_PAYMENT' end end when po.order_status = 7 then case when po.purchase_type = 2 then case when po.apply_pass_time is null then 'PROCUREMENT_ORDER_CREATED,PROCUREMENT_WAITING_FOR_DELIVERY,PROCUREMENT_PENDING_PAYMENT,PROCUREMENT_TRANSACTION_COMPLETE' else 'PROCUREMENT_ORDER_CREATED,PROCUREMENT_WAIT_APPROVE,PROCUREMENT_WAITING_FOR_DELIVERY,PROCUREMENT_PENDING_PAYMENT,PROCUREMENT_TRANSACTION_COMPLETE' end else case when po.apply_pass_time is null then 'PROCUREMENT_ORDER_CREATED,PROCUREMENT_PENDING_PAYMENT,PROCUREMENT_WAITING_FOR_DELIVERY,PROCUREMENT_TRANSACTION_COMPLETE' else 'PROCUREMENT_ORDER_CREATED,PROCUREMENT_WAIT_APPROVE,PROCUREMENT_PENDING_PAYMENT,PROCUREMENT_WAITING_FOR_DELIVERY,PROCUREMENT_TRANSACTION_COMPLETE' end end when po.order_status = 8 then case when po.purchase_type =2 then case when po.apply_pass_time is null then case when po.in_stock_time is null then 'PROCUREMENT_ORDER_CREATED,PROCUREMENT_TRANSACTION_CLOSE' else case when bo.finance_code is not null && (select count(*) from super4s_finance.finance_order where status in (12,22) and finance_code = bo.finance_code) =1 then 'PROCUREMENT_ORDER_CREATED,PROCUREMENT_WAITING_FOR_DELIVERY,PROCUREMENT_PENDING_PAYMENT,PROCUREMENT_TRANSACTION_CLOSE' else 'PROCUREMENT_ORDER_CREATED,PROCUREMENT_WAITING_FOR_DELIVERY,PROCUREMENT_TRANSACTION_CLOSE' end end else case when po.in_stock_time is null then 'PROCUREMENT_ORDER_CREATED,PROCUREMENT_WAIT_APPROVE,PROCUREMENT_TRANSACTION_CLOSE' else case when bo.finance_code is not null && (select count(*) from super4s_finance.finance_order where status in (12,22) and finance_code = bo.finance_code) =1 then 'PROCUREMENT_ORDER_CREATED,PROCUREMENT_WAIT_APPROVE,PROCUREMENT_WAITING_FOR_DELIVERY,PROCUREMENT_PENDING_PAYMENT,PROCUREMENT_TRANSACTION_CLOSE' else 'PROCUREMENT_ORDER_CREATED,PROCUREMENT_WAIT_APPROVE,PROCUREMENT_WAITING_FOR_DELIVERY,PROCUREMENT_TRANSACTION_CLOSE' end end end else case when po.apply_pass_time is null then case when bo.finance_code is not null && (select count(*) from super4s_finance.finance_order where status in (12,22) and finance_code = bo.finance_code) =1 then case when po.in_stock_time is null then 'PROCUREMENT_ORDER_CREATED,PROCUREMENT_PENDING_PAYMENT,PROCUREMENT_TRANSACTION_CLOSE' else 'PROCUREMENT_ORDER_CREATED,PROCUREMENT_PENDING_PAYMENT,PROCUREMENT_WAITING_FOR_DELIVERY,PROCUREMENT_TRANSACTION_CLOSE' end else 'PROCUREMENT_ORDER_CREATED,PROCUREMENT_TRANSACTION_CLOSE' end else case when bo.finance_code is not null && (select count(*) from super4s_finance.finance_order where status in (12,22) and finance_code = bo.finance_code) =1 then case when po.in_stock_time is null then 'PROCUREMENT_ORDER_CREATED,PROCUREMENT_WAIT_APPROVE,PROCUREMENT_PENDING_PAYMENT,PROCUREMENT_TRANSACTION_CLOSE' else 'PROCUREMENT_ORDER_CREATED,PROCUREMENT_WAIT_APPROVE,PROCUREMENT_PENDING_PAYMENT,PROCUREMENT_WAITING_FOR_DELIVERY,PROCUREMENT_TRANSACTION_CLOSE' end else 'PROCUREMENT_ORDER_CREATED,PROCUREMENT_WAIT_APPROVE,PROCUREMENT_TRANSACTION_CLOSE' end end end end ) where bo.order_type = 1 and bo.base_order_code = po.base_order_code;
2、優化知道思想
- 儘量使SQL短而小,更趨於原子化。如果一個sql特別耗時,達到一定時間後,會被系統kill掉;另外,大sql執行時,後面的sql處於阻塞
狀態,這樣會佔用很多的系統資源; - 減少case巢狀的深度。
3、優化後的SQL
因為是根據procurement_order.order_status
的不同值(0-11)來給base_order.done_status
賦值。所以我們首先是根據order_status對procurement_order
的資料量做了統計。對於某一狀態,如果資料量相對比較少,我們就不拆分sql,只有對資料量大的sql我們進行拆分。
-- 1.1.1、base_order採購單狀態進度條——非完成、關閉狀態 update super4s_order.base_order bo, super4s_order.procurement_order po set bo.done_status = ( case when po.order_status = 0 then 'PROCUREMENT_ORDER_CREATED' when po.order_status = 1 then 'PROCUREMENT_ORDER_CREATED,PROCUREMENT_WAIT_APPROVE' when po.order_status in (2,3) then 'PROCUREMENT_ORDER_CREATED,PROCUREMENT_WAIT_APPROVE,PROCUREMENT_TRANSACTION_CLOSE' when po.order_status in (11,5,10) then case when po.purchase_type = 2 then case when po.apply_pass_time is null then 'PROCUREMENT_ORDER_CREATED,PROCUREMENT_WAITING_FOR_DELIVERY' else 'PROCUREMENT_ORDER_CREATED,PROCUREMENT_WAIT_APPROVE,PROCUREMENT_WAITING_FOR_DELIVERY' end else case when po.apply_pass_time is null then 'PROCUREMENT_ORDER_CREATED' else 'PROCUREMENT_ORDER_CREATED,PROCUREMENT_WAIT_APPROVE' end end when po.order_status in (6,9) then case when po.purchase_type = 2 then case when po.apply_pass_time is null then 'PROCUREMENT_ORDER_CREATED' else 'PROCUREMENT_ORDER_CREATED,PROCUREMENT_WAIT_APPROVE' end else case when po.apply_pass_time is null then 'PROCUREMENT_ORDER_CREATED,PROCUREMENT_PENDING_PAYMENT' else 'PROCUREMENT_ORDER_CREATED,PROCUREMENT_WAIT_APPROVE,PROCUREMENT_PENDING_PAYMENT' end end end ) where bo.order_type = 1 and bo.base_order_code = po.base_order_code;
-- 1.1.2、base_order採購單狀態進度條——狀態 update super4s_order.base_order bo, super4s_order.procurement_order po set bo.done_status = ( case when po.purchase_type = 2 then case when po.apply_pass_time is null then 'PROCUREMENT_ORDER_CREATED,PROCUREMENT_WAITING_FOR_DELIVERY,PROCUREMENT_PENDING_PAYMENT,PROCUREMENT_TRANSACTION_COMPLETE' else 'PROCUREMENT_ORDER_CREATED,PROCUREMENT_WAIT_APPROVE,PROCUREMENT_WAITING_FOR_DELIVERY,PROCUREMENT_PENDING_PAYMENT,PROCUREMENT_TRANSACTION_COMPLETE' end when po.purchase_type = 5 then 'PROCUREMENT_ORDER_CREATED,PROCUREMENT_TRANSACTION_COMPLETE' else case when po.apply_pass_time is null then 'PROCUREMENT_ORDER_CREATED,PROCUREMENT_PENDING_PAYMENT,PROCUREMENT_WAITING_FOR_DELIVERY,PROCUREMENT_TRANSACTION_COMPLETE' else 'PROCUREMENT_ORDER_CREATED,PROCUREMENT_WAIT_APPROVE,PROCUREMENT_PENDING_PAYMENT,PROCUREMENT_WAITING_FOR_DELIVERY,PROCUREMENT_TRANSACTION_COMPLETE' end end ) where bo.order_type = 1 and po.order_status = 7 and bo.base_order_code = po.base_order_code;
-- 1.1.3、base_order採購單狀態進度條——關閉狀態&寄售
update
super4s_order.base_order bo,
super4s_order.procurement_order po
set
bo.done_status = (
case
when po.apply_pass_time is null then
case
when po.in_stock_time is null then 'PROCUREMENT_ORDER_CREATED,PROCUREMENT_TRANSACTION_CLOSE'
else
case
when bo.finance_code is not null && (select count(1) from super4s_finance.finance_order where finance_code = bo.finance_code and status in (12,22)) =1
then 'PROCUREMENT_ORDER_CREATED,PROCUREMENT_WAITING_FOR_DELIVERY,PROCUREMENT_PENDING_PAYMENT,PROCUREMENT_TRANSACTION_CLOSE'
else 'PROCUREMENT_ORDER_CREATED,PROCUREMENT_WAITING_FOR_DELIVERY,PROCUREMENT_TRANSACTION_CLOSE'
end
end
else
case
when po.in_stock_time is null then 'PROCUREMENT_ORDER_CREATED,PROCUREMENT_WAIT_APPROVE,PROCUREMENT_TRANSACTION_CLOSE'
else
case
when bo.finance_code is not null && (select count(1) from super4s_finance.finance_order where finance_code = bo.finance_code and status in (12,22)) =1
then 'PROCUREMENT_ORDER_CREATED,PROCUREMENT_WAIT_APPROVE,PROCUREMENT_WAITING_FOR_DELIVERY,PROCUREMENT_PENDING_PAYMENT,PROCUREMENT_TRANSACTION_CLOSE'
else 'PROCUREMENT_ORDER_CREATED,PROCUREMENT_WAIT_APPROVE,PROCUREMENT_WAITING_FOR_DELIVERY,PROCUREMENT_TRANSACTION_CLOSE'
end
end
end
)
where
bo.order_type = 1
and
po.order_status = 8
and
po.purchase_type =2
and
bo.base_order_code = po.base_order_code;
-- 1.1.4、base_order採購單狀態進度條——關閉狀態&非寄售
update
super4s_order.base_order bo,
super4s_order.procurement_order po
set
bo.done_status = (
case
when po.apply_pass_time is null then
case
when bo.finance_code is not null && (select count(1) from super4s_finance.finance_order where finance_code = bo.finance_code and status in (12,22)) =1
then
case
when po.in_stock_time is null
then 'PROCUREMENT_ORDER_CREATED,PROCUREMENT_PENDING_PAYMENT,PROCUREMENT_TRANSACTION_CLOSE'
else 'PROCUREMENT_ORDER_CREATED,PROCUREMENT_PENDING_PAYMENT,PROCUREMENT_WAITING_FOR_DELIVERY,PROCUREMENT_TRANSACTION_CLOSE'
end
else 'PROCUREMENT_ORDER_CREATED,PROCUREMENT_TRANSACTION_CLOSE'
end
else
case
when bo.finance_code is not null && (select count(1) from super4s_finance.finance_order where finance_code = bo.finance_code and status in (12,22)) =1
then
case
when po.in_stock_time is null
then 'PROCUREMENT_ORDER_CREATED,PROCUREMENT_WAIT_APPROVE,PROCUREMENT_PENDING_PAYMENT,PROCUREMENT_TRANSACTION_CLOSE'
else 'PROCUREMENT_ORDER_CREATED,PROCUREMENT_WAIT_APPROVE,PROCUREMENT_PENDING_PAYMENT,PROCUREMENT_WAITING_FOR_DELIVERY,PROCUREMENT_TRANSACTION_CLOSE'
end
else 'PROCUREMENT_ORDER_CREATED,PROCUREMENT_WAIT_APPROVE,PROCUREMENT_TRANSACTION_CLOSE'
end
end
)
where
bo.order_type = 1
and
po.order_status = 8
and
po.purchase_type != 2
and
bo.base_order_code = po.base_order_code;
4、成果
優化前,35萬的資料執行了35分鐘;優化後,5秒內執行完畢了!如果各位對這個sql優化還有其他方面的觀點,歡迎留言。