[SQL SERVER][Performance]避免區域變數

[SQL SERVER][Performance]避免區域變數

今天在SQL PASS TW上看到一個查詢效能問題

個人3月份分享查詢調校不求人有提過,撰寫TSQL要盡量避免區域變數,

因為當你使用區域變數時,查詢最佳化程式無法知道編譯時期(compile time)該值是多少,

所以會造成統計值誤用估算誤差很大,估算誤差越大將選用到不好的執行計畫,

因為該區域變數值只能在執行時期(Runtime)得知,

但如果你一定要使用區域變數怎麼辦?

是否可以讓查詢最佳化程式知道編譯時期的區域變數呢?

答案是可以的,讓我簡單模擬該網友問題並解決它。

 

使用區域變數宣告

declare @startId nvarchar(30),@endid nvarchar(30)
set @startId =N'Y980006618239'
set @endid=N'Y980006767250'
select BILL_NO,POLICY_NO,BILL_STATUS_CODE
from BILL
where BILL_NO>=@startId and BILL_NO<=@endid

 

image

logical reads=13379 、整體執行時間 281+120 =401ms。

 

 

image

執行計畫中,可以看到實際資料列數目和估計相差很大。

 

image

整體成本12.0983。

 

如我前面所說,因為使用區域變數,所以編譯時期無法得知該變數值(只能在執行期間得知),

這將造成統計值誤差變大(實際值只有78筆,但估算比數卻高達180000),估計筆數讓查詢最佳化程式以為是大結果集,

而且查詢成本超過一般序列執行計畫成本,所以採用平行計畫來處理並選擇不良執行計畫,

導致整體成本拉高(整體執行時間拉長)、邏輯讀取變多。

 

直接帶值不使用區域變數

select BILL_NO,POLICY_NO,BILL_STATUS_CODE
from BILL
where BILL_NO>=N'Y980006618239' and BILL_NO<='Y980006767250' 

 

image

logical reads=13 。

 

 

image

執行計畫中可以看到這次選擇clustered index。

 

 

image

整體成本0.0032831。

 

直接帶值可以讓查詢最佳化程式在compile time知道該值內容,

這樣可以比較精準透過統計值來估算相關資料筆數,並且選用較佳執行計畫,

所以你在撰寫TSQL時,應該要盡量避免區域變數的使用,

但我就是想使用,是否有辦法解決呢?請看下面

 

 

使用 sp_executesql 執行

declare @startId nvarchar(30),@endid nvarchar(30)
set @startId =N'Y980006618239'
set @endid=N'Y980006767250'
declare @myquery nvarchar(4000); 
set @myquery = 'select BILL_NO,POLICY_NO,BILL_STATUS_CODE
from BILL
where BILL_NO>=@startId and BILL_NO<=@endid';
execute sp_executesql @myquery,N'@startId nvarchar(30) ,@endid nvarchar(30)',
@startId,@endid 

image

logical reads=13 。

 

 

image

執行計畫和直接帶值相同。

 

 

 

image

整體成本0.0032831也和直接帶值相同。