SQL Server-聚焦什么時候用OPTION(COMPILE)呢运沦?

摘要:?上一篇我們探討了在靜態(tài)語句中使用 WHERE Column=@Param OR @Param IS NULL的問題,有對OPTION(COMPILE)的評論配深,那這節(jié)我們來探討OPTION(COMPILE)的問題携添。

上一篇我們探討了在靜態(tài)語句中使用

WHERE Column=@Param OR @Param IS NULL的問題,有對OPTION(COMPILE)的評論篓叶,那這節(jié)我們來探討OPTION(COMPILE)的問題烈掠。

探討OPTION(COMPILE)問題

在SQL SERVER中任何時候第一次調用存儲過程時羞秤,此時存儲過程將會被SQL SERVER優(yōu)化且查詢計劃在內(nèi)存中會被緩存。由于查詢計劃緩存左敌,當運行相同的存儲過程時瘾蛋,它都將使用相同的查詢計劃,從而無需每次運行時對同一存儲過程進行優(yōu)化和編譯矫限。因此瘦黑,如果我們需要每天運行相同的存儲過程若干次,那么可以節(jié)省大量的時間和硬件資源奇唤。

如果每次運行的存儲過程中的在WHERE子句中具有相同的參數(shù)幸斥,則重復使用存儲過程的相同查詢計劃是有意義的。但是咬扇,如果運行相同的存儲過程甲葬,但是參數(shù)的值會改變呢?發(fā)生什么取決于參數(shù)的典型性懈贺。如果存儲過程的參數(shù)的值從執(zhí)行到執(zhí)行相似经窖,那么緩存的查詢計劃將正常工作,查詢將按照執(zhí)行最佳來梭灿。但是画侣,如果參數(shù)不是典型的,那么被重用的緩存查詢計劃可能不是最優(yōu)的堡妒,導致查詢運行更慢配乱,因為它使用的查詢計劃并不是真正為所使用的參數(shù)設計的。下面我們借助AdventureWorks2012實例數(shù)據(jù)庫來用實例講解上述所描述的情況皮迟。

DECLARE@AddressLine1NVARCHAR(60) =NULL,? ? @AddressLine2NVARCHAR(60) =NULL,? ? @CityNVARCHAR(30) =NULL,? ? @PostalCodeNVARCHAR(15) =NULL,? ? @StateProvinceIDINT=NULLSET@City ='Bothell'SET@PostalCode ='98011'SET@StateProvinceID =79DECLARE@SQLNVARCHAR(MAX),@ColumnNameVARCHAR(4000),@ParamDefinitionNVARCHAR(500)SET@ColumnName ='a.AddressID, a.AddressLine1, a.AddressLine2, a.City, a.StateProvinceID, a.PostalCode, a.rowguid'SET@SQL='SELECT '+ @ColumnName +' FROM Person.Address AS a WHERE 1 = 1'IF(@AddressLine1ISNOTNULL)SET@SQL= @SQL+' AND a.AddressLine1 LIKE ''%'' + @AddressLine1 + ''%'''IF(@AddressLine2ISNOTNULL)SET@SQL= @SQL+' AND a.AddressLine2 LIKE ''%'' + @AddressLine2 + ''%'''IF(@CityISNOTNULL)SET@SQL= @SQL+' AND a.City LIKE ''%'' + @City + ''%'''IF(@PostalCodeISNOTNULL)SET@SQL= @SQL+' AND a.PostalCode LIKE ''%'' + @PostalCode + ''%'''IF(@StateProvinceIDISNOTNULL)SET@SQL= @SQL+' AND a.StateProvinceID = @StateProvinceID'SET@ParamDefinition = N'@AddressLine1 NVARCHAR(60),

? ? ? ? ? ? ? ? ? ? ? ? @AddressLine2 NVARCHAR(60),

? ? ? ? ? ? ? ? ? ? ? ? @City NVARCHAR(30),

? ? ? ? ? ? ? ? ? ? ? ? @PostalCode NVARCHAR(15),

? ? ? ? ? ? ? ? ? ? ? ? @StateProvinceID INT'EXECUTEsp_executesql @SQL,@ParamDefinition,? ? ? ? ? ? ? ? ? ? ? ? ? @AddressLine1? = @AddressLine1,? ? ? ? ? ? ? ? ? ? ? ? ? @AddressLine2 = @AddressLine2,? ? ? ? ? ? ? ? ? ? ? ? ? @City = @City,? ? ? ? ? ? ? ? ? ? ? ? ? @PostalCode = @PostalCode,? ? ? ? ? ? ? ? ? ? ? ? ? @StateProvinceID = @StateProvinceIDGO

我們運行上述查詢1次搬泥,看到查詢結果如下和計劃緩存次數(shù)如下:

此時我們將外部變量StateProvinceID類型修改為SMALLINT,然后再來運行查詢和緩存計劃伏尼,此時會出現(xiàn)查詢計劃使用次數(shù)是為2忿檩,還是出現(xiàn)兩條次數(shù)都為1呢?

此時我們再來將動態(tài)SQL中內(nèi)部變量StateProvinceID類型修改為SMALLINT爆阶,此時會出現(xiàn)查詢計劃使用次數(shù)是為3燥透,還是出現(xiàn)兩條,次數(shù)分別為2和1呢辨图?

由上可知班套,如果我們修改外部變量參數(shù)類型不會影響查詢計劃緩存即會達到重用目的,若修改動態(tài)SQL內(nèi)部變量參數(shù)類型則不會重用查詢計劃緩存徒役。

大多數(shù)情況下孽尽,我們可能不需要擔心上述問題窖壕。但是忧勿,在某些情況下杉女,假設從查詢的執(zhí)行到執(zhí)行的參數(shù)變化很大,則會引起問題鸳吸。

如果我們確定存儲過程通常運行正常熏挎,但有時運行緩慢,則很可能會看到上述問題晌砾。在這種情況下坎拐,我們可以做的是改變存儲過程,并添加WITH RECOMPILE選項养匈。

添加此選項后哼勇,存儲過程將始終重新編譯自身,并在每次運行時創(chuàng)建一個新的查詢計劃呕乎。當然這會消除查詢計劃重用的好處积担,但確保了每次運行查詢時都使用正確的查詢計劃。如果存儲過程中有多個查詢猬仁,那么它將重新編譯存儲過程中的所有查詢帝璧,即使那些不受非典型參數(shù)影響的查詢也是如此。

講完OPTION(COMPILE)湿刽,接下來我們講講如何創(chuàng)建高性能的存儲過程的烁。有些童鞋可能會創(chuàng)建如下存儲過程。

CREATEPROC [dbo].[HighPerformanceExample](? ? @AddressLine1NVARCHAR(60) =NULL,? ? @AddressLine2NVARCHAR(60) =NULL,? ? @CityNVARCHAR(30) =NULL,? ? @PostalCodeNVARCHAR(15) =NULL,? ? @StateProvinceIDSMALLINT=NULL)ASSETNOCOUNTONSELECTa.AddressID, a.AddressLine1, a.AddressLine2, a.City, a.StateProvinceID, a.PostalCode, a.rowguidFROMPerson.AddressASaWHERE(a.AddressLine1 = @AddressLine1OR@AddressLine1ISNULL)AND(a.AddressLine2 = @AddressLine2OR@AddressLine2ISNULL)AND(a.City = @CityOR@CityISNULL)AND(a.PostalCode = @PostalCodeOR@PostalCodeISNULL)AND(a.StateProvinceID = @StateProvinceIDOR@StateProvinceIDISNULL)--或者SELECTa.AddressID, a.AddressLine1, a.AddressLine2, a.City, a.StateProvinceID, a.PostalCode, a.rowguidFROMPerson.AddressASaWHEREa.AddressLine1 =COALESCE(@AddressLine1, a.AddressLine1)ANDa.AddressLine2 =COALESCE(@AddressLine2, a.AddressLine2)ANDa.City =COALESCE(@City, a.City)ANDa.PostalCode =COALESCE(@PostalCode, a.PostalCode)ANDa.StateProvinceID =COALESCE(@StateProvinceID, a.StateProvinceID)--或者SELECTa.AddressID, a.AddressLine1, a.AddressLine2, a.City, a.StateProvinceID, a.PostalCode, a.rowguidFROMPerson.AddressASaWHEREa.AddressLine1 =CASEWHEN@AddressLine1ISNULLTHENa.AddressLine1ELSE@AddressLine1ENDANDa.AddressLine2 =CASEWHEN@AddressLine2ISNULLTHENa.AddressLine1ELSE@AddressLine2ENDANDa.City =CASEWHEN@CityISNULLTHENa.CityELSE@CityENDANDa.PostalCode =CASEWHEN@PostalCodeISNULLTHENa.PostalCodeELSE@PostalCodeENDANDa.StateProvinceID =CASEWHEN@StateProvinceIDISNULLTHENa.StateProvinceIDELSE@StateProvinceIDENDGOSETNOCOUNTOFF

上述無論怎樣執(zhí)行都將表現(xiàn)的非常糟糕诈闺。因為SQL SERVER不能將其很好地進行優(yōu)化渴庆,如果這是由不同的參數(shù)組合產(chǎn)生,那么我們可能會得到一個絕對糟糕的計劃雅镊。不難理解把曼,當執(zhí)行一個存儲過程,并且還沒有生成一個查詢緩存計劃漓穿。所以嗤军,管理員可能會更新統(tǒng)計信息或強制重新編譯(或者,甚至重新啟動SQL Server)來嘗試解決此問題晃危,但這些都不是最佳解決方案叙赚。OPTION(COMPILE)重新編譯是個好東西,但是我們是不是像如下簡單加上重新編譯就可以了呢僚饭。

SELECT...FROM...WHERE...OPTION(RECOMPILE);

如果我們要使用重新編譯震叮,那么我們是否需要考慮以下兩個問題呢?

如果我們知道一個特定的語句總是返回相同數(shù)量的行并使用相同的計劃(并且我們已測試過并知道這一點)鳍鸵,那么我們會正常創(chuàng)建存儲過程并讓計劃得到緩存苇瓣。

如果我們知道一個特定的語句從執(zhí)行到執(zhí)行是不一樣的,最佳查詢計劃也會有所不同(我們也應該從執(zhí)行多個測試樣本中知道這一點)偿乖,然后我們會如正常一樣創(chuàng)建存儲過程击罪,然后使用OPTION(RECOMPILE)以確保語句的計劃不會被存儲過程緩存或保存哲嘲。在每次執(zhí)行時,存儲過程將獲得不同的參數(shù)媳禁,如此一來語句將在每次執(zhí)行時得到一個新的計劃眠副。

上述已經(jīng)描述的很明朗了,使用或者不使用重新編譯的前提不過是需不需要查詢計劃緩存還是重新生成一個查詢計劃竣稽,但是我們怎么知道到底是否需要查詢計劃緩存呢囱怕?這就要看對創(chuàng)建的存儲過程是否穩(wěn)定了,如果穩(wěn)定我們就從緩存中去取毫别,否則使用重新編譯查詢娃弓。歸根結底一句話概述:重新編譯不穩(wěn)定(可變)的計劃,但為穩(wěn)定(不可變)的計劃岛宦,我們把它們放在緩存中重用忘闻。

為了實現(xiàn)這點,我們需要分析所查詢的存儲過程恋博,例如在每個企業(yè)下有對應的用戶齐佳,我們想象一下所呈現(xiàn)的UI界面,首先是所有用戶债沮,查詢條件則是企業(yè)下拉框炼吴,然后是用戶名或者員工工號等。當沒有任何篩選條件時則走查詢計劃緩存疫衩,若選擇企業(yè)硅蹦,或者還選擇了員工相關篩選條件則重新編譯。類似如下存儲過程闷煤。

CREATEPROC [dbo].[HighPerformanceExample](? ? @AddressLine1NVARCHAR(60) =NULL,? ? @AddressLine2NVARCHAR(60) =NULL,? ? @CityNVARCHAR(30) =NULL,? ? @PostalCodeNVARCHAR(15) =NULL,? ? @StateProvinceIDSMALLINT=NULL)ASSETNOCOUNTONDECLARE@SQLNVARCHAR(MAX),@ColumnNameVARCHAR(4000),@ParamDefinitionNVARCHAR(500),@RecompileBIT=1;SET@ColumnName ='a.AddressID, a.AddressLine1, a.AddressLine2, a.City, a.StateProvinceID, a.PostalCode, a.rowguid'SET@SQL='SELECT '+ @ColumnName +' FROM Person.Address AS a WHERE 1 = 1'IF(@StateProvinceIDISNOTNULL)SET@SQL= @SQL+' AND a.StateProvinceID = @StateProvinceID'IF(@AddressLine1ISNOTNULL)SET@SQL= @SQL+' AND a.AddressLine1 LIKE @AddressLine1'IF(@AddressLine2ISNOTNULL)SET@SQL= @SQL+' AND a.AddressLine2 LIKE @AddressLine2'IF(@CityISNOTNULL)SET@SQL= @SQL+' AND a.City LIKE @City'IF(@PostalCodeISNOTNULL)SET@SQL= @SQL+' AND a.PostalCode LIKE @PostalCode'IF(@StateProvinceIDISNOTNULL)SET@Recompile =0IF(PATINDEX('%[%_?]%',@AddressLine1) >=4ORPATINDEX('%[%_?]%', @AddressLine2) =0)AND(PATINDEX('%[%_?]%', @City) >=4ORPATINDEX('%[%_?]%', @PostalCode) =0)SET@Recompile =0IF@Recompile =1BEGINSET@SQL= @SQL+ N' OPTION(RECOMPILE)';END;SET@ParamDefinition = N'@AddressLine1 NVARCHAR(60),

? ? ? ? ? ? ? ? ? ? ? ? @AddressLine2 NVARCHAR(60),

? ? ? ? ? ? ? ? ? ? ? ? @City NVARCHAR(30),

? ? ? ? ? ? ? ? ? ? ? ? @PostalCode NVARCHAR(15),

? ? ? ? ? ? ? ? ? ? ? ? @StateProvinceID SMALLINT'EXECUTEsp_executesql @SQL,@ParamDefinition,? ? ? ? ? ? ? ? ? ? ? ? ? @AddressLine1? = @AddressLine1,? ? ? ? ? ? ? ? ? ? ? ? ? @AddressLine2 = @AddressLine2,? ? ? ? ? ? ? ? ? ? ? ? ? @City = @City,? ? ? ? ? ? ? ? ? ? ? ? ? @PostalCode = @PostalCode,? ? ? ? ? ? ? ? ? ? ? ? ? @StateProvinceID = @StateProvinceIDGOSETNOCOUNTOFF

本節(jié)我們講解了如何在存儲過程中使用OPTION(COMPILE)童芹,并且使得存儲過程性能達到最佳,我想這是根據(jù)實際場景分析存儲過程相對來說首選和最佳的方法鲤拿,以至于我們不必每次都重新編譯假褪。從而給我們長期更好的可擴展性。

版權聲明:本文內(nèi)容由互聯(lián)網(wǎng)用戶自發(fā)貢獻近顷,版權歸作者所有生音,本社區(qū)不擁有所有權,也不承擔相關法律責任窒升。如果您發(fā)現(xiàn)本社區(qū)中有涉嫌抄襲的內(nèi)容缀遍,歡迎發(fā)送郵件至:yqgroup@service.aliyun.com?進行舉報,并提供相關證據(jù)饱须,一經(jīng)查實域醇,本社區(qū)將立刻刪除涉嫌侵權內(nèi)容。

原文鏈接

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市譬挚,隨后出現(xiàn)的幾起案子锅铅,更是在濱河造成了極大的恐慌,老刑警劉巖殴瘦,帶你破解...
    沈念sama閱讀 222,627評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件狠角,死亡現(xiàn)場離奇詭異号杠,居然都是意外死亡蚪腋,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,180評論 3 399
  • 文/潘曉璐 我一進店門姨蟋,熙熙樓的掌柜王于貴愁眉苦臉地迎上來屉凯,“玉大人,你說我怎么就攤上這事眼溶∮蒲猓” “怎么了?”我有些...
    開封第一講書人閱讀 169,346評論 0 362
  • 文/不壞的土叔 我叫張陵堂飞,是天一觀的道長灌旧。 經(jīng)常有香客問我,道長绰筛,這世上最難降的妖魔是什么枢泰? 我笑而不...
    開封第一講書人閱讀 60,097評論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮铝噩,結果婚禮上衡蚂,老公的妹妹穿的比我還像新娘。我一直安慰自己骏庸,他們只是感情好毛甲,可當我...
    茶點故事閱讀 69,100評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著具被,像睡著了一般玻募。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上一姿,一...
    開封第一講書人閱讀 52,696評論 1 312
  • 那天补箍,我揣著相機與錄音,去河邊找鬼啸蜜。 笑死坑雅,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的衬横。 我是一名探鬼主播裹粤,決...
    沈念sama閱讀 41,165評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了遥诉?” 一聲冷哼從身側響起拇泣,我...
    開封第一講書人閱讀 40,108評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎矮锈,沒想到半個月后霉翔,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,646評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡苞笨,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,709評論 3 342
  • 正文 我和宋清朗相戀三年债朵,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片瀑凝。...
    茶點故事閱讀 40,861評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡序芦,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出粤咪,到底是詐尸還是另有隱情谚中,我是刑警寧澤,帶...
    沈念sama閱讀 36,527評論 5 351
  • 正文 年R本政府宣布寥枝,位于F島的核電站宪塔,受9級特大地震影響,放射性物質發(fā)生泄漏囊拜。R本人自食惡果不足惜某筐,卻給世界環(huán)境...
    茶點故事閱讀 42,196評論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望艾疟。 院中可真熱鬧来吩,春花似錦、人聲如沸蔽莱。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,698評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽盗冷。三九已至怠苔,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間仪糖,已是汗流浹背柑司。 一陣腳步聲響...
    開封第一講書人閱讀 33,804評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留锅劝,地道東北人攒驰。 一個月前我還...
    沈念sama閱讀 49,287評論 3 379
  • 正文 我出身青樓,卻偏偏與公主長得像故爵,于是被迫代替她去往敵國和親玻粪。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,860評論 2 361

推薦閱讀更多精彩內(nèi)容