如何用參數化SQL語句污染你的計劃緩存

你的SQL語句的參數化老是個好想法。使用參數化SQL語句你不會污染你的計劃緩存——錯!!!在這篇文章裏我想向你展現下用參數化SQL語句就能夠污染你的計劃緩存,這是很是簡單的!sql

ADO.NET-AddWithValue

ADO.NET是實現像SQL Server關係數據庫數據訪問的.NET框架的組成——有一些嚴重的反作用。不要誤解我——只要你正確使用,ADO.NET一直很棒。你立刻就會看到,它很容易被錯誤使用。咱們來看下面實現SQL語句執行的C#代碼。 數據庫

 1 for (int i = 1; i <= 100; i++)
 2 {
 3    val += i.ToString();
 4 
 5    cmd = new SqlCommand(
 6       "SELECT * FROM Sales.SalesOrderDetail WHERE CarrierTrackingNumber = @CarrierTrackingNumber", 
 7       cnn);
 8    cmd.Parameters.AddWithValue("@CarrierTrackingNumber", val);
 9    SqlDataReader reader = cmd.ExecuteReader();
10    reader.Close();
11 }

咱們是聰明的開發者,所以SQL語句自己被參數化,由於ADO.NET框架是地球上最棒的框架,咱們使用System.Data.SqlClient.SqlParameterCollection類的AddWithValue方法來提供實際的參數值。我在WHLIE循環裏運行那個SQL語句100次,總用不一樣長度賦予參數值。在Sales.SalesOrderDetail表裏CarrierTrackingNumber列定義爲NVARCHAR(25)。所以咱們能夠在基於咱們提供的不一樣字符長度上有上至25個不一樣數據類型的參數。如今讓咱們檢查下咱們SQL語句執行後的計劃緩存。緩存

1 SELECT
2     st.text,
3     cp.*
4 FROM sys.dm_exec_cached_plans cp
5 CROSS APPLY sys.dm_exec_sql_text(cp.plan_handle) st
6 GO

如今事情變得有點瘋狂:在計劃緩存裏咱們存儲了100個不一樣的執行計劃!框架

 

對於每一個可能的數據類型參數都有1個執行計劃——即便當數據類型是NVACHAR(25)AddWithValue方法很是,很是邪惡:基於你提供的參數值派生出數據類型。永遠不要使用它!spa

ADO.NET – SqlDbType.VarChar

由於從咱們的錯誤中咱們學到了,如今咱們知道ADO.NET的AddWithValue方法的反作用——咱們再也不用它。如今讓咱們重寫咱們的C#程序代碼,以下所示定義一個顯示的參數數據類型: 3d

 1 for (int i = 1; i <= 100; i++)
 2 {
 3    val += i.ToString();
 4 
 5    cmd = new SqlCommand(
 6       "SELECT * FROM Sales.SalesOrderDetail WHERE CarrierTrackingNumber = @CarrierTrackingNumber",
 7       cnn);
 8    cmd.Parameters.Add(new SqlParameter("@CarrierTrackingNumber", SqlDbType.VarChar));
 9    cmd.Parameters["@CarrierTrackingNumber"].Value = val;
10    SqlDataReader reader = cmd.ExecuteReader();
11    reader.Close();
12 }

從代碼裏你能夠看到,ADO.NET如今不能派生參數數據類型了,由於咱們已經指定了SqlDbType.Varchar數據類型。讓咱們再次執行這個SQL語句100次並再次檢查下計劃緩存:code

 

沒有啥改變。問題仍是同樣:在計劃緩存裏咱們還有100個不同的的執行計劃。如今的問題是ADO.NET只強制數據類型(SqlDbType.VarChar),但不是數據類型的"長度"。有100個不一樣的長度在計劃緩存裏你就有100個不一樣的執行計劃。blog

若是你在你的ADO.NET代碼裏顯式指定參數數據類型,你也要指定它的長度!如今咱們來看下一些修正的C#代碼。開發

 1 for (int i = 1; i <= 100; i++)
 2 {
 3    val += i.ToString();
 4 
 5    cmd = new SqlCommand(
 6       "SELECT * FROM Sales.SalesOrderDetail WHERE CarrierTrackingNumber = @CarrierTrackingNumber",
 7       cnn);
 8    cmd.Parameters.Add(new SqlParameter("@CarrierTrackingNumber", SqlDbType.VarChar, 100));
 9    cmd.Parameters["@CarrierTrackingNumber"].Value = val;
10    SqlDataReader reader = cmd.ExecuteReader();
11    reader.Close();
12 }

此次我也指定了數據類型的長度——這裏是100,如今當咱們再次執行SQL語句100次時,最後咱們在計劃緩存裏以1個執行計劃且重用了100次來完美收工。這是從SQL Server角度的最終目標。get

 

小結

寓意:ADO.NET是個很棒的數據訪問框架,它提供你有用的功能(例如AddWithValue方法),當從SQL Server角度來講你真的要考慮下你在作什麼。當你使用參數化SQL語句時,你要儘可能顯式:你必須地冠以參數值的實際數據類型,還有你想要的得到數據類型長度。

感謝關注! 

參考文章:

https://www.sqlpassion.at/archive/2015/07/20/how-to-pollute-your-plan-cache-with-parameterized-sql-statements/

相關文章
相關標籤/搜索