曾經我也感覺我不會寫出執行耗時特別長的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優化還有其他方面的觀點,歡迎留言。
