SQLServer 參數(shù)化查詢經(jīng)驗分享
一個簡單理解參數(shù)化查詢的方式是把它看做只是一個T-SQL查詢,它接受控制這個查詢返回什么的參數(shù)。通過使用不同的參數(shù),一個參數(shù)化查詢返回不同的結(jié)果。要獲得一個參數(shù)化查詢,你需要以一種特定的方式來編寫你的代碼,或它需要滿足一組特定的標(biāo)準(zhǔn)。
有兩種不同的方式來創(chuàng)建參數(shù)化查詢。第一個方式是讓查詢優(yōu)化器自動地參數(shù)化你的查詢。另一個方式是通過以一個特定方式來編寫你的T-SQL代碼,并將它傳遞給sp_executesql系統(tǒng)存儲過程,從而編程一個參數(shù)化查詢。這篇文章的后面部分將介紹這個方法。
參數(shù)化查詢的關(guān)鍵是查詢優(yōu)化器將創(chuàng)建一個可以重用的緩存計劃。通過自動地或編程使用參數(shù)化查詢,SQL Server可以優(yōu)化類似T-SQL語句的處理。這個優(yōu)化消除了對使用高貴資源為這些類似T-SQL語句的每一次執(zhí)行創(chuàng)建一個緩存計劃的需求。而且通過創(chuàng)建一個可重用計劃,SQL Server還減少了存放過程緩存中類似的執(zhí)行計劃所需的內(nèi)存使用。
現(xiàn)在讓我們看看使得SQL Server創(chuàng)建參數(shù)化查詢的不同方式。
參數(shù)化查詢是怎樣自動創(chuàng)建的?
微軟編寫查詢優(yōu)化器代碼的人竭盡全力地優(yōu)化SQL Server處理你的T-SQL命令的方式。我想這是查詢優(yōu)化器名稱的由來。這些盡量減少資源和最大限度地提高查詢優(yōu)化器執(zhí)行性能的方法之一是查看一個T-SQL語句并確定它們是否可以被參數(shù)化。要了解這是如何工作的,讓我們看看下面的T-SQL語句:
SELECT* FROMAdventureWorks.Sales.SalesOrderHeader WHERESalesOrderID=56000; GO |
如果你使用下面的SELECT語句來查看一個只包含用于上面語句的緩存計劃的、干凈的緩沖池,那么你會看到查詢優(yōu)化器將T-SQL查詢重寫為一個參數(shù)化T-SQL語句:
SELECTstats.execution_countAScnt, p.size_in_bytesAS[size], [sql].[text]AS[plan_text] FROMsys.dm_exec_cached_plansp OUTERAPPLYsys.dm_exec_sql_text(p.plan_handle)sql JOINsys.dm_exec_query_statsstats ONstats.plan_handle=p.plan_handle; GO |
cnt size plan_text
--- ------- --------------------------------------------------------------
1 49152 (@1 int)SELECT * FROM [AdventureWorks].[Sales].[SalesOrderHeader]
WHERE [SalesOrderID]=@1
如果你看看上面輸出中的plan_text字段,你會看到它不像原來的T-SQL文本。如前所述,查詢優(yōu)化器將這個查詢重新編寫為一個參數(shù)化T-SQL語句。在這里,你可以看到它現(xiàn)在有一個數(shù)據(jù)類型為(int)的變量(@1),它在之前的SELECT語句中被定義的。另外在plan_text的末尾, 值“56000”被替換為變量@1。既然這個T-SQL語句被重寫了,而且被存儲為一個緩存計劃,那么如果未來一個T-SQL命令和它大致相同,只有SalesOrderID字段被賦的值不同的話,它就可以被用于重用。讓我們在動作中看看它。
如果我在我的機器上運行下面的命令:
DBCCFREEPROCCACHE; GO SELECT* FROMAdventureWorks.Sales.SalesOrderHeader WHERESalesOrderID=56000; GO SELECT* FROMAdventureWorks.Sales.SalesOrderHeader WHERESalesOrderID=56001; GO SELECTstats.execution_countAScnt, p.size_in_bytesAS[size], [sql].[text]AS[plan_text] FROMsys.dm_exec_cached_plansp OUTERAPPLYsys.dm_exec_sql_text(p.plan_handle)sql JOINsys.dm_exec_query_statsstats ONstats.plan_handle=p.plan_handle; GO 我從最后的SELECT語句得到下面的輸出,(注意,輸出被重新格式化以便它更易讀): cntsizeplan_text ------------------------------------------------------------------------- 249152(@1int)SELECT*FROMAdventureWorks].[Sales].[SalesOrderHeader] WHERE[SalesOrderID]=@1 |
在這里,我首先釋放過程緩存,然后我執(zhí)行兩個不同、但卻類似的非參數(shù)化查詢來看看查詢優(yōu)化器是會創(chuàng)建兩個不同的緩存計劃還是創(chuàng)建用于這兩個查詢的一個緩存計劃。在這里,你可以看到查詢優(yōu)化器事實上很聰明,它參數(shù)化第一個查詢并緩存了計劃。然后當(dāng)?shù)诙€類似、但有一個不同的SalesOrderID值的查詢發(fā)送到SQL Server時,優(yōu)化器可以識別已經(jīng)緩存了一個計劃,然后重用它來處理第二個查詢。你可以這么說是因為“cnt”字段現(xiàn)在表明這個計劃被用了兩次。
數(shù)據(jù)庫配置選項PARAMETERIZATION可以影響T-SQL語句怎樣被自動地參數(shù)化。對于這個選項有兩種不同的設(shè)置,SIMPLE和FORCED。當(dāng)PARAMETERIZATION設(shè)置被設(shè)置為SIMPLE時,只有簡單的T-SQL語句才會被參數(shù)化。要介紹這個,看下下面的命令:
SELECTSUM(LineTotal)ASLineTotal FROMAdventureWorks.Sales.SalesOrderHeaderH JOINAdventureWorks.Sales.SalesOrderDetailDOND.SalesOrderID=H.SalesOrderID WHEREH.SalesOrderID=56000 |
這個查詢類似于我前面的示例,除了在這里我添加了一個額外的JOIN標(biāo)準(zhǔn)。當(dāng)數(shù)據(jù)庫AdventureWorks的PARAMETERIZATION選項被設(shè)置為SIMPLE時,這個查詢不會被自動地參數(shù)化。SIMPLE PARAMETERIZATION設(shè)置告訴查詢優(yōu)化器只參數(shù)化簡單的查詢。但是當(dāng)選項PARAMETERIZATION被設(shè)置為FORCED時,這個查詢將被自動地參數(shù)化。
當(dāng)你設(shè)置數(shù)據(jù)庫選項為使用FORCE PARAMETERIZATION時,查詢優(yōu)化器試圖參數(shù)化所有的查詢,而不僅僅是簡單的查詢。你可能會認為這很好。但是在某些情況下,當(dāng)數(shù)據(jù)庫設(shè)置PARAMETERIZATION為FORCED時,查詢優(yōu)化器將選擇不是很理想的查詢計劃。當(dāng)數(shù)據(jù)庫設(shè)置PARAMETER為FORCED時,它改變查詢中的字面常量。這可能導(dǎo)致當(dāng)查詢中涉及計算字段時索引和索引視圖不被選中參與到執(zhí)行計劃中,從而導(dǎo)致一個無效的計劃。FORCED PARAMETERIZATION選項可能是改進具有大量類似的、傳遞過來的參數(shù)稍有不同的查詢的數(shù)據(jù)庫性能的一個很好的解決方案。一個在線銷售應(yīng)用程序,它的客戶對你的產(chǎn)品執(zhí)行大量的類似搜索, 產(chǎn)品值不同,這可能是一個能夠受益于FORCED PARAMETERIZATION的很好的應(yīng)用程序類型。
不是所有的查詢從句都會被參數(shù)化。例如查詢的TOP、TABLESAMPLE、 HAVING、GROUP BY、ORDER BY、OUTPUT...INTO或FOR XML從句不會被參數(shù)化。使用sp_execute_sql來參數(shù)化你的T-SQL
你不需要依賴于數(shù)據(jù)庫的PARAMETERIZATION選項來使得查詢優(yōu)化器參數(shù)化一個查詢。你可以參數(shù)化你自己的查詢。你通過重新編寫你的T-SQL語句并使用“sp_executesql”系統(tǒng)存儲過程執(zhí)行重寫的語句來實現(xiàn)。正如已經(jīng)看到的,上面包括一個“JOIN”從句的SELECT語句在數(shù)據(jù)庫的PARAMETERIZATION設(shè)置為SIMPLE時沒有被自動參數(shù)化。讓我重新編寫這個查詢以便查詢優(yōu)化器將創(chuàng)建一個可重用的參數(shù)化查詢執(zhí)行計劃。
為了說明,讓我們看兩個類似的、不會被自動參數(shù)化的T-SQL語句,并創(chuàng)建兩個不同的緩存執(zhí)行計劃。然后我將重新編寫這兩個查詢使得它們都使用相同的緩存參數(shù)化執(zhí)行計劃。
讓我們看看這個代碼:
DBCCFREEPROCCACHE GO SELECTSUM(LineTotal)ASLineTotal FROMAdventureWorks.Sales.SalesOrderHeaderH JOINAdventureWorks.Sales.SalesOrderDetailDOND.SalesOrderID=H.SalesOrderID WHEREH.SalesOrderID=56000 GO SELECTSUM(LineTotal)ASLineTotal FROMAdventureWorks.Sales.SalesOrderHeaderH JOINAdventureWorks.Sales.SalesOrderDetailDOND.SalesOrderID=H.SalesOrderID WHEREH.SalesOrderID=56001 GO SELECTstats.execution_countAScnt, p.size_in_bytesAS[size], LEFT([sql].[text],200)AS[plan_text] FROMsys.dm_exec_cached_plansp OUTERAPPLYsys.dm_exec_sql_text(p.plan_handle)sql JOINsys.dm_exec_query_statsstatsONstats.plan_handle=p.plan_handle; GO |
在這里,我釋放了過程緩存,然后運行這兩個包含一個JOIN的、不同的非簡單的T-SQL語句。然后我將檢查緩存計劃。這是這個使用DMV 的SELECT語句的輸出(注意,輸出被重新格式化了,以便它更易讀):
cntsizeplan_text --------------------------------------------------------------------------------------------- 149152SELECTSUM(LineTotal)ASLineTotal FROMAdventureWorks.Sales.SalesOrderHeaderH JOINAdventureWorks.Sales.SalesOrderDetailD OND.SalesOrderID=H.SalesOrderID WHEREH.SalesOrderID=56001 149152SELECTSUM(LineTotal)ASLineTotal FROMAdventureWorks.Sales.SalesOrderHeaderH JOINAdventureWorks.Sales.SalesOrderDetailD OND.SalesOrderID=H.SalesOrderID WHEREH.SalesOrderID=56000 |
下面是上面的代碼被重新編寫來使用sp_executesql 系統(tǒng)存儲過程:
DBCCFREEPROCCACHE; GO EXECsp_executesqlN'SELECTSUM(LineTotal)ASLineTotal FROMAdventureWorks.Sales.SalesOrderHeaderH JOINAdventureWorks.Sales.SalesOrderDetailDOND.SalesOrderID=H.SalesOrderID WHEREH.SalesOrderID=@SalesOrderID',N'@SalesOrderIDINT',@SalesOrderID=56000; GO EXECsp_executesqlN'SELECTSUM(LineTotal)ASLineTotal FROMAdventureWorks.Sales.SalesOrderHeaderH JOINAdventureWorks.Sales.SalesOrderDetailDOND.SalesOrderID=H.SalesOrderID WHEREH.SalesOrderID=@SalesOrderID',N'@SalesOrderIDINT',@SalesOrderID=56001; GO SELECTstats.execution_countASexec_count, p.size_in_bytesAS[size], [sql].[text]AS[plan_text] FROMsys.dm_exec_cached_plansp OUTERAPPLYsys.dm_exec_sql_text(p.plan_handle)sql JOINsys.dm_exec_query_statsstatsONstats.plan_handle=p.plan_handle; GO |
現(xiàn)在當(dāng)我運行上面的代碼時,我從DMV SELECT語句得到下面的輸出(注意,輸出被重新格式化了,以便它更易讀):
cntsizeplan_text ------------------------------------------------------------------------------------------------------- 249152(@SalesOrderIDINT)SELECTSUM(LineTotal)ASLineTotal FROMAdventureWorks.Sales.SalesOrderHeaderH JOINAdventureWorks.Sales.SalesOrderDetailDOND.SalesOrderID=H.SalesOrderID WHEREH.SalesOrderID=@SalesOrderID |
從這個輸出,你可以看出,我有一個參數(shù)化緩存計劃,它被執(zhí)行了兩次,為每個EXEC語句各執(zhí)行了一次。
使用參數(shù)化查詢來節(jié)省資源和優(yōu)化性能
在語句可以被執(zhí)行之前,每個T-SQL語句都需要被評估,而且需要建立一個執(zhí)行計劃。創(chuàng)建執(zhí)行計劃會占用寶貴的CPU資源。當(dāng)執(zhí)行計劃被創(chuàng)建后,它使用內(nèi)存空間將它存儲在過程緩存中。降低CPU和內(nèi)存使用的一個方法是利用參數(shù)化查詢。盡管數(shù)據(jù)庫可以被設(shè)置為對所有查詢FORCE參數(shù)化,但是這不總是最好的選擇。通過了解你的哪些T-SQL語句可以被參數(shù)化然后使用sp_executesql存儲過程,你可以幫助SQL Server節(jié)省資源并優(yōu)化你的查詢的性能。
版權(quán)聲明:本站文章來源標(biāo)注為YINGSOO的內(nèi)容版權(quán)均為本站所有,歡迎引用、轉(zhuǎn)載,請保持原文完整并注明來源及原文鏈接。禁止復(fù)制或仿造本網(wǎng)站,禁止在非www.sddonglingsh.com所屬的服務(wù)器上建立鏡像,否則將依法追究法律責(zé)任。本站部分內(nèi)容來源于網(wǎng)友推薦、互聯(lián)網(wǎng)收集整理而來,僅供學(xué)習(xí)參考,不代表本站立場,如有內(nèi)容涉嫌侵權(quán),請聯(lián)系alex-e#qq.com處理。