1. 程式人生 > 實用技巧 >SQL優化——SQL語句拆分

SQL優化——SQL語句拆分

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