筆記51-徐 參數嗅探 Parameter Sniffing


筆記51-徐 參數嗅探 Parameter Sniffing 

  1 --參數嗅探 Parameter Sniffing  2013-2-8
  2 
  3 --當使用存儲過程的時候,總是要使用到一些變量。變量有兩種,一種
  4 --是在存儲過程的外面定義的。當調用存儲過程的時候,必須要給他代入
  5 --值。這種變量,SQL在編譯的時候知道他的值是多少。
  6 
  7 --例如:
  8 USE [AdventureWorks]
  9 GO
 10 DROP PROC Sniff
 11 GO
 12 CREATE PROC Sniff(@i INT)
 13 AS
 14 SELECT COUNT(b.[SalesOrderID]),SUM(p.[Weight])
 15 FROM [dbo].[SalesOrderHeader_test] a
 16 INNER JOIN [dbo].[SalesOrderDetail_test] b
 17 ON a.[SalesOrderID]=b.[SalesOrderID]
 18 INNER JOIN [Production].[Product] p
 19 ON b.[ProductID]=p.[ProductID]
 20 WHERE a.[SalesOrderID]=@i
 21 GO
 22 
 23 --這里的變量@i,就是要在調用的時候代入值的
 24 EXEC [dbo].[Sniff] @i = 50000 -- int
 25 GO
 26 
 27 
 28 --還有一種變量是在存儲過程里面定義的。他的值在存儲過程的語句執行的過程中得到的。
 29 --所以對這種本地變量,SQL在編譯的時候不知道他的值是多少。
 30 --例如:
 31 USE [AdventureWorks]
 32 GO
 33 DROP PROC Sniff2
 34 GO
 35 CREATE PROC Sniff2(@i INT)
 36 AS
 37 DECLARE @j INT
 38 SET @j=@i
 39 SELECT COUNT(b.[SalesOrderID]),SUM(p.[Weight])
 40 FROM [dbo].[SalesOrderHeader_test] a
 41 INNER JOIN [dbo].[SalesOrderDetail_test] b
 42 ON a.[SalesOrderID]=b.[SalesOrderID]
 43 INNER JOIN [Production].[Product] p
 44 ON b.[ProductID]=p.[ProductID]
 45 WHERE a.[SalesOrderID]=@j
 46 GO
 47 
 48 EXEC [dbo].[Sniff2] @i = 500000 -- int
 49 GO
 50 
 51 
 52 
 53 --這里的變量@j,是SQL在運行的過程中算出來的
 54 --已經談到過多次,SQL在處理存儲過程的時候,為了節省編譯時間,
 55 --是一次編譯,多次重用的。用sp_executesql的方式調用的指令也是
 56 --這樣。那么執行計划重用就有兩個潛在問題
 57 
 58 
 59 --(1)對於第一類變量,根據第一次運行時代入的值生成的執行計划,是不是
 60 --就能夠適合所有可能的變量值呢?
 61 
 62 
 63 --(2)對於第二類本地變量,SQL在編譯的時候並不知道他的值是多少,那怎么
 64 --選擇“合適”的執行計划呢?
 65 
 66 --對於第一個問題,會引出對 “參數嗅探”問題的定義。而對於第二個問題,本節
 67 --將介紹使用本地變量對執行計划選擇的影響。最后介紹參數嗅探問題的候選
 68 --解決方案
 69 
 70 
 71 ------------------------------------------------------------------------
 72 --什么是參數嗅探
 73 --帶着第一個問題,請做下面這兩個測試
 74 --測試一:
 75 USE [AdventureWorks]
 76 GO
 77 DBCC freeproccache
 78 GO
 79 EXEC [dbo].[Sniff] @i = 500000 -- int
 80 --發生編譯,插入一個使用nested loops聯接的執行計划
 81 GO
 82 
 83 EXEC [dbo].[Sniff] @i = 75124 -- int
 84 --發生執行計划重用,重用上面的nested loops的執行計划
 85 GO
 86 
 87 --測試二:
 88 
 89 USE [AdventureWorks]
 90 GO
 91 DBCC freeproccache
 92 GO
 93 EXEC [dbo].[Sniff] @i = 75124 -- int
 94 --發生編譯,插入一個使用hash match聯接的執行計划
 95 GO
 96 
 97 EXEC [dbo].[Sniff] @i = 50000 -- int
 98 --發生執行計划重用,重用上面的hash match的執行計划
 99 GO
100 
101 
102 --從上面兩個測試可以清楚地看到執行計划重用的副作用。由於數據分布差別很大
103 --參數50000和75124只對自己生成的執行計划有好的性能,如果使用對方生成的
104 --執行計划,性能就會下降。參數50000返回的結果集比較小,所以性能下降
105 --不太嚴重。參數75124返回的結果集大,就有了明顯的性能下降,兩個執行計划
106 --的差別有近10倍
107 
108 --對於這種因為重用他人生成的執行計划而導致的水土不服現象,SQL有一個專有
109 --名詞,叫“參數嗅探 parameter sniffing”是因為語句的執行計划對變量的值
110 --很敏感,而導致重用執行計划會遇到性能問題
111 
112 
113 --本地變量的影響
114 --那對於有parameter sniffing問題的存儲過程,如果使用本地變量,會怎樣呢?
115 --下面請看測試3。這次用不同的變量值時,都清空執行計划緩存,迫使其
116 --重編譯
117 --第一次
118 USE [AdventureWorks]
119 GO
120 DBCC freeproccache
121 GO
122 SET STATISTICS TIME ON
123 SET STATISTICS PROFILE ON
124 EXEC [dbo].[Sniff] @i = 50000 -- int
125 GO
126 ------------------------------
127 --第二次
128 USE [AdventureWorks]
129 GO
130 DBCC freeproccache
131 GO
132 SET STATISTICS TIME ON
133 SET STATISTICS PROFILE ON
134 EXEC [dbo].[Sniff] @i = 75124 -- int
135 GO
136 --------------------------------
137 --第三次
138 USE [AdventureWorks]
139 GO
140 DBCC freeproccache
141 GO
142 SET STATISTICS TIME ON
143 SET STATISTICS PROFILE ON
144 EXEC [dbo].[Sniff2] @i = 50000 -- int
145 GO
146 ---------------------------------
147 --第四次
148 USE [AdventureWorks]
149 GO
150 DBCC freeproccache
151 GO
152 SET STATISTICS TIME ON
153 SET STATISTICS PROFILE ON
154 EXEC [dbo].[Sniff2] @i = 75124 -- int
155 GO
156 
157 
158 --來看他們的執行計划:
159 --對於第一句和第二句,因為SQL在編譯的時候知道變量的值,所以在做EstimateRows的時候,
160 --做得非常准確,選擇了最適合他們的執行計划
161 
162 
163 --但是對於第三句和第四句,SQL不知道@j的值是多少,所以在做EstimateRows的時候,
164 --不管代入的@i值是多少,一律給@j一樣的預測結果。所以兩個執行計划是完全一樣的。
165 --這里EstimateRows的大小,在語句1和語句2的值之間,所以他選擇的執行計划,和
166 --語句1與語句2都不一樣
167 
168 --我們再來比較一下不同執行計划下的速度
169 --------------------------------------
170 --第一次
171 SQL Server 執行時間:
172    CPU 時間 = 0 毫秒,占用時間 = 715 毫秒。
173 
174 SQL Server 執行時間:
175    CPU 時間 = 16 毫秒,占用時間 = 1775 毫秒。
176 --------------------------------------------
177 --------------------------------------
178 --第二次
179 SQL Server 執行時間:
180    CPU 時間 = 998 毫秒,占用時間 = 9821 毫秒。
181 
182 SQL Server 執行時間:
183    CPU 時間 = 998 毫秒,占用時間 = 9906 毫秒。
184 --------------------------------------------
185 --------------------------------------
186 --第三次
187 SQL Server 執行時間:
188    CPU 時間 = 15 毫秒,占用時間 = 57 毫秒。
189 
190 SQL Server 執行時間:
191    CPU 時間 = 15 毫秒,占用時間 = 66 毫秒。
192 --------------------------------------------
193 --------------------------------------
194 --第四次
195 SQL Server 執行時間:
196    CPU 時間 = 1981 毫秒,占用時間 = 6926 毫秒。
197 
198 SQL Server 執行時間:
199    CPU 時間 = 1997 毫秒,占用時間 = 6933 毫秒。
200 --------------------------------------------
201 
202 --有參數嗅探的情況,語句三和語句四作出來的執行計划是一種比較中庸的方法,不是最快的
203 --也不是最慢的。他對語句性能的影響,一般不會有參數嗅探那么嚴重,他還是解決
204 --參數嗅探的一個候選方案
205 
206 
207 ---------------------------------------------------------------------------------
208 --參數嗅探的解決方案
209 
210 --參數嗅探的問題發生的頻率並不高,他只會發生在一些表格里的數據分布很不均勻,或者
211 --用戶帶入的參數值很不均勻的情況下。例如,查詢一個時間段數據的存儲過程,如果
212 --大部分用戶都只查1天的數據,SQL緩存的也是這樣的執行計划,那對於那些要查一年
213 --的數據,可是SQL碰到“參數嗅探”問題的風險了。如果系統里大部分用戶都要查一年的數據
214 --可是SQL碰巧緩存了一個只查一天數據的存儲過程,那大部分用戶都會遇到“參數嗅探”的問題
215 --這個對性能的影響就大了
216 
217 --有什么辦法能夠緩解,或者避免參數嗅探問題呢?在SQL2005以后,可以有很多種方法可供
218 --選擇
219 
220 --(1)用exec()的方式運行動態SQL
221 --如果在存儲過程里不是直接運行語句,而是把語句帶上變量,生成一個字符串,再讓exec()這樣
222 --的命令做動態語句運行,那SQL就會在運行到這句話的時候,對動態語句進行編譯。這時SQL
223 --已經知道了變量的值,會根據生成優化的執行計划,從而繞過參數嗅探問題
224 
225 --例如前面的存儲過程Sniff,就可以改成這樣
226 USE [AdventureWorks]
227 GO
228 DROP PROC NOSniff
229 GO
230 CREATE PROC NOSniff(@i INT)
231 AS
232 DECLARE @cmd VARCHAR(1000)
233 SET @cmd='SELECT COUNT(b.[SalesOrderID]),SUM(p.[Weight])
234 FROM [dbo].[SalesOrderHeader_test] a
235 INNER JOIN [dbo].[SalesOrderDetail_test] b
236 ON a.[SalesOrderID]=b.[SalesOrderID]
237 INNER JOIN [Production].[Product] p
238 ON b.[ProductID]=p.[ProductID]
239 WHERE a.[SalesOrderID]='
240 EXEC(@cmd+@i)
241 GO
242 
243 --然后再用上面的順序調用會得到比他更好的性能
244 
245 USE [AdventureWorks]
246 GO
247 DBCC freeproccache
248 GO
249 SET STATISTICS TIME ON
250 SET STATISTICS PROFILE ON
251 EXEC [dbo].NOSniff @i = 50000 -- int
252 GO
253 --------------------------------
254 USE [AdventureWorks]
255 GO
256 SET STATISTICS TIME ON
257 SET STATISTICS PROFILE ON
258 EXEC [dbo].NOSniff @i = 75124 -- int
259 GO
260 
261 
262 
263 --使用exec()的方式產生動態編譯
264 --在查詢語句執行之前,都能看見SP:CacheInsert事件。SQL做了動態編譯,根據變量的值,
265 --正確地估計出每一步的返回結果集大小。所以這兩個執行計划,是完全不一樣的
266 
267 --執行結果很快,這種方法的好處,是徹底避免參數嗅探問題。但缺點是要修改存儲過程
268 --的定義,而且放棄了存儲過程一次編譯,多次運行的優點,在編譯性能上有所損失
269 
270 
271 --(2)使用本地變量local variable
272 --在前面,提到了如果把變量值賦給一個本地變量,SQL在編譯的時候是沒辦法知道這個本地
273 --變量的值的。所以他會根據表格里數據的一般分布情況,“猜測”一個返回值。不管用戶
274 --在調用存儲過程的時候代入的變量值是多少,做出來的執行計划都是一樣的。而這樣
275 --的執行計划一般比較“中庸”,不會是最優的執行計划,但是對大多數變量值來講,也不會
276 --是一個很差的執行計划
277 
278 --存儲過程[Sniff2]就是這樣一個例子
279 --這種方法的好處,是保持了存儲過程的優點,缺點是要修改存儲過程,而且執行計划也不是
280 --最優的
281 
282 
283 
284 --(3)在語句里使用query hint,指定執行計划
285 --在select,insert,update,delete語句的最后,可以加一個"option(<query_hint>)"的子句
286 --對SQL將要生成的執行計划進行指導。當DBA知道問題所在以后,可以通過加hint的方式,引導
287 --SQL生成一個比較安全的,對所有可能的變量值都不差的執行計划
288 
289 --現在SQL的query hint還是很強大的,有十幾種hint。完整的定義是:
290 --msdn:http://msdn.microsoft.com/zh-cn/library/foo66fb1520-dcdf-4aab-9ff1-7de8f79e5b2d.aspx
291 <query_hint > ::=
292 { { HASH | ORDER } GROUP
293   | { CONCAT | HASH | MERGE } UNION
294   | { LOOP | MERGE | HASH } JOIN
295   | FAST number_rows
296   | FORCE ORDER
297   | MAXDOP number_of_processors
298   | OPTIMIZE FOR ( @variable_name = literal_constant [ , ...n ] )
299   | PARAMETERIZATION { SIMPLE | FORCED }
300   | RECOMPILE
301   | ROBUST PLAN
302   | KEEP PLAN
303   | KEEPFIXED PLAN
304   | EXPAND VIEWS
305   | MAXRECURSION number
306   | USE PLAN N'xml_plan'
307 }
308 
309 
310 --這些hint的用途不一樣。有些是引導執行計划使用什么樣的運算的,比如,
311 --  | { CONCAT | HASH | MERGE } UNION
312 --  | { LOOP | MERGE | HASH } JOIN
313 
314 --有些是防止重編譯的,例如
315 --  | PARAMETERIZATION { SIMPLE | FORCED }
316 --  | KEEP PLAN
317 --  | KEEPFIXED PLAN
318 
319 
320 --有些是強制重編譯的,例如
321 --  | RECOMPILE
322 
323 
324 --有些是影響執行計划的選擇的,例如
325 --| FAST number_rows
326 --| FORCE ORDER
327 --| MAXDOP number_of_processors
328 --| OPTIMIZE FOR ( @variable_name = literal_constant [ , ...n ] )
329 
330 --所以他們適合在不同的場合。具體的定義,請看SQL的聯機幫助
331 
332 --為了避免參數嗅探的問題,有下面幾種常見的query hint使用方法
333 
334 --(1)recompile
335 --recompile這個查詢提示告訴SQL,語句在每一次存儲過程運行的時候,都要重新編譯一下。
336 --這樣能夠使SQL根據當前變量的值,選一個最好的執行計划。對前面的那個例子,我們可以
337 --這樣寫
338 USE [AdventureWorks]
339 GO
340 DROP PROC NoSniff_QueryHint_Recompile
341 GO
342 CREATE PROC NoSniff_QueryHint_Recompile(@i INT)
343 AS
344 SELECT COUNT(b.[SalesOrderID]),SUM(p.[Weight])
345 FROM [dbo].[SalesOrderHeader_test] a
346 INNER JOIN [dbo].[SalesOrderDetail_test] b
347 ON a.[SalesOrderID]=b.[SalesOrderID]
348 INNER JOIN [Production].[Product] p
349 ON b.[ProductID]=p.[ProductID]
350 WHERE a.[SalesOrderID]=@i
351 OPTION(RECOMPILE)
352 GO
353 
354 --------------------------------------
355 USE [AdventureWorks]
356 GO
357 DBCC freeproccache
358 GO
359 EXEC NoSniff_QueryHint_Recompile 50000
360 GO
361 -------------------------------------
362 USE [AdventureWorks]
363 GO
364 EXEC NoSniff_QueryHint_Recompile 75124
365 GO
366 
367 
368 --在SQL Trace里我們能夠看到,語句運行之前,都會有一個SQL:StmtRecompile的事件發生
369 --而使用的執行計划,就是最准確的那種
370 --和這種方法類似,是在存儲過程的定義里直接指定“recompile”,也能達到避免
371 --參數嗅探的效果
372 
373 USE [AdventureWorks]
374 GO
375 DROP PROC NoSniff_SPCreate_Recompile
376 GO
377 CREATE PROC NoSniff_SPCreate_Recompile(@i INT)
378 WITH RECOMPILE
379 AS
380 SELECT COUNT(b.[SalesOrderID]),SUM(p.[Weight])
381 FROM [dbo].[SalesOrderHeader_test] a
382 INNER JOIN [dbo].[SalesOrderDetail_test] b
383 ON a.[SalesOrderID]=b.[SalesOrderID]
384 INNER JOIN [Production].[Product] p
385 ON b.[ProductID]=p.[ProductID]
386 WHERE a.[SalesOrderID]=@i
387 GO
388 
389 
390 -------------------------------------------------
391 USE [AdventureWorks]
392 GO
393 DBCC freeproccache
394 GO
395 EXEC NoSniff_SPCreate_Recompile 50000
396 GO
397 
398 ------------------------------------------------
399 USE [AdventureWorks]
400 GO
401 EXEC NoSniff_QueryHint_Recompile 75124
402 GO
403 
404 
405 --在SQL Trace里,我們能看到,存儲過程在執行的時候已經找不到前面的執行計划
406 --SP:CacheMiss,所以要生成新的。而使用的執行計划,就是最准確的那種
407 
408 --這兩種“Recompile”提示的差別是,如果在語句層次指定OPTION(RECOMPILE),
409 --那存儲過程級別的計划重用還是會有的。只是在運行到那句話的時候,才會發生重編譯。
410 --如果存儲過程里有if-else之類的邏輯,使得發生問題的語句沒有執行到,那重編譯就不會發生。
411 --所以,這是一種問題精確定位后,比較精細的一種調優方法。如果在存儲過程級別
412 --指定WITH RECOMPILE,那整個存儲過程在每次執行的時候都要重編譯,這個重復工作量就比較大了
413 --但是,如果問題沒有精確定位,可以用這種方法快速緩解問題
414 
415 
416 
417 --(2)指定join運算 { LOOP | MERGE | HASH } JOIN
418 --很多時候,參數嗅探問題是由於SQL對一個該用merge join/hash join的情況誤用了
419 --nested loops join。確定了問題后,當然可以用查詢提示,指定語句里所有join方法。
420 --但是這種方法一般很不推薦,因為不是所有的join,SQL都能夠根據你給的提示做出來
421 --執行計划的。如果對例子存儲過程的那個查詢最后加上“option(hash join)”,運行
422 --的時候會返回這樣的錯誤。SQL不接受這個查詢提示:
423 
424 消息 8622,級別 16,狀態 1,第 1425 由於此查詢中定義了提示,查詢處理器未能生成查詢計划。請重新提交查詢,
426 並且不要在查詢中指定任何提示,也不要使用 SET FORCEPLAN。
427 
428 
429 --更常見的是,在特定的那個join上使用join hint。這種方法成功幾率要高得多
430 --例如:
431 USE [AdventureWorks]
432 GO
433 DROP PROC NoSniff_QueryHint_JoinHint
434 GO
435 CREATE PROC NoSniff_QueryHint_JoinHint(@i INT)
436 AS
437 SELECT COUNT(b.[SalesOrderID]),SUM(p.[Weight])
438 FROM [dbo].[SalesOrderHeader_test] a
439 INNER JOIN [dbo].[SalesOrderDetail_test] b
440 ON a.[SalesOrderID]=b.[SalesOrderID]
441 INNER hash JOIN [Production].[Product] p
442 ON b.[ProductID]=p.[ProductID]
443 WHERE a.[SalesOrderID]=@i
444 GO
445 
446 
447 USE [AdventureWorks]
448 GO
449 DBCC freeproccache
450 GO
451 SET STATISTICS PROFILE ON
452 GO
453 EXEC NoSniff_QueryHint_JoinHint 50000
454 GO
455 ---------------------------------------------
456 USE [AdventureWorks]
457 GO
458 SET STATISTICS PROFILE ON
459 GO
460 EXEC NoSniff_QueryHint_JoinHint 75124
461 GO
462 
463 
464 
465 --這種方法的好處,是保持了存儲過程一次編譯,后續多次使用的特性,節省編譯時間。
466 --但是缺點也很明顯,使用這樣的方法生成執行計划,不一定就是一個好的執行計划
467 --而且表格里的數據量變化以后,現在合適的join方式將來可能就不合適,到時候還會
468 --造成性能問題,所以使用的時候要很小心
469 
470 
471 
472 
473 --(3)OPTIMIZE FOR ( @variable_name = literal_constant [ , ...n ] )
474 --當確認了參數嗅探問題后,發現,根據某些變量值生成的執行計划,快和慢會相差
475 --很大,而根據另外一些變量生成的執行計划,性能在好和壞的時候,相差並不很大。
476 --例如當變量等於50000的時候,他用最好的nested loops執行計划,用時十幾毫秒,
477 --用hash join的那個執行計划,也不過300多毫秒。但是變量等於75124的時候,
478 --hash join執行計划需要500多毫秒,用nested loops的時候,要用4000多。
479 --從絕對值來講,讓用戶等幾百毫秒一般是沒問題的。但是等上幾秒鍾,就容易
480 --收到抱怨了。所以hash join是一個比較“安全”的執行計划。如果SQL總是使用75124
481 --這個值做執行計划,會對大部分查詢都比較安全。
482 
483 --使用OPTIMIZE FOR這個查詢指導,就能夠讓SQL做到這一點。這是SQL2005以后的一個新
484 --功能。
485 
486 USE [AdventureWorks]
487 GO
488 DROP PROC NoSniff_QueryHint_OptimizeFor
489 GO
490 CREATE PROC NoSniff_QueryHint_OptimizeFor(@i INT)
491 AS
492 SELECT COUNT(b.[SalesOrderID]),SUM(p.[Weight])
493 FROM [dbo].[SalesOrderHeader_test] a
494 INNER JOIN [dbo].[SalesOrderDetail_test] b
495 ON a.[SalesOrderID]=b.[SalesOrderID]
496 INNER hash JOIN [Production].[Product] p
497 ON b.[ProductID]=p.[ProductID]
498 WHERE a.[SalesOrderID]=@i
499 OPTION(optimize FOR(@i=75124))
500 GO
501 ---------------------------------
502 USE [AdventureWorks]
503 GO
504 DBCC freeproccache
505 GO
506 SET STATISTICS PROFILE ON
507 GO
508 EXEC NoSniff_QueryHint_OptimizeFor 50000
509 GO
510 ---------------------------------------------
511 USE [AdventureWorks]
512 GO
513 SET STATISTICS PROFILE ON
514 GO
515 EXEC NoSniff_QueryHint_OptimizeFor 75124
516 GO
517 
518 
519 --從SQL Trace里看,存儲過程第一次運行時有編譯,但是第二次就出現了執行計划重用
520 --分析執行計划會發現SQL在第一次編譯時,雖然變量值是50000,他還是根據75124
521 --來做預估的,EstimateRows的值很大。正因為這樣,第二次運行也有不錯的性能
522 
523 --這種方法的優點是,既能夠讓SQL作出有傾向性的執行計划,又能保證SQL選擇執行計划
524 --時候的自由度,所以得到的執行計划一般是比較好的。相對於用join hint,這個方法
525 --更精細一些。缺點是,如果表格里的數據分布發生了變化,比如用戶有一天把
526 --75124的記錄全刪除了,那SQL選擇的執行計划就不一定繼續正確了。所以他也有他
527 --的局限性。
528 
529 
530 --上面介紹的方法,都有一個明顯的局限性,那就要去修改存儲過程定義。有些時候沒有
531 --應用開發組的許可,修改存儲過程是不可以的。對用sp_executesql的方式調用的指令,
532 --問題更大。因為這些指令可能是寫在應用程序里,而不是SQL里。DBA是沒辦法去修改
533 --應用程序的。
534 
535 --好在SQL2005和2008里,引入和完善了一個種叫Plan Guide的功能。DBA可以告訴SQL,當
536 --運行某一個語句的時候,請你使用我指定的執行計划。這樣就不需要去修改存儲過程或
537 --應用了。
538 
539 
540 
541 --(4)Plan Guide
542 --例如可以用下面的方法,在原來那個有參數嗅探問題的存儲過程“Sniff”上,解決sniffing問題
543 USE [AdventureWorks]
544 GO
545 EXEC [sys].[sp_create_plan_guide]
546 @name=N'Guide1',
547 @stmt=N'SELECT COUNT(b.[SalesOrderID]),SUM(p.[Weight])
548 FROM [dbo].[SalesOrderHeader_test] a
549 INNER JOIN [dbo].[SalesOrderDetail_test] b
550 ON a.[SalesOrderID]=b.[SalesOrderID]
551 INNER JOIN [Production].[Product] p
552 ON b.[ProductID]=p.[ProductID]
553 WHERE a.[SalesOrderID]=@i',
554 @type=N'OBJECT',
555 @module_or_batch=N'Sniff',
556 @params=NULL,
557 @hints=N'option(optimize for(@i=75124))';
558 GO
559 
560 -----------------------------------
561 USE [AdventureWorks]
562 GO
563 SET STATISTICS PROFILE ON
564 GO
565 DBCC freeproccache
566 GO
567 EXEC [dbo].[Sniff] @i = 50000 -- int
568 GO
569 
570 --使用了75124的hash match的join方式
571 
572 --對於Plan Guide,他還可以使用在一般的語句調優里。在后面的章節會介紹
573 
574 
575 -----------------------各種方法的比較-----------------------------------
576 --      方法                 是否修改存儲過程             是否每次運行都要重編譯          執行計划准確度
577 --用exec()方式運行動態SQL          需要                        會                             很准確
578 --使用本地變量local variable       需要                        不會                            一般
579 --query hint+"recompile"           需要                        會                              很准確
580 --query  hint指定join運算          需要                        不會                            很一般
581 --query hint optimize for          需要                        不會                            比較准確
582 --Plan Guide                       不需要                      不會                            比較准確
583 
584 
585 --DBA可以根據實際情況,選擇對他最合適的解決方案

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM