其實這是一篇沒有技術含量的文章,精通SQL優化的請繞道。這個緣起于在優化一個SQL過程中,同僚問了我一個問題,為什麼SQL中存在隐式轉換,但是執行計劃沒有變? 我思索了一下,覺得這個問題也有點意思,說不定有些對隐式轉換了解得不深入的同學都有此疑問,那麼下面結合上下文場景做一個細節方面的解答。
我們一個系統中使用了ORMLite架構,粗心的開發人員弄出了不少下面這樣的SQL語句,都存在隐式轉換問題,如下所示,表machine_stop_alarm_msg 的結構如下,字段machine_no、status都為VARCHAR(10),但是下面SQL,傳入的變量@P0,@P1都是NVARCHAR(4000)類型。
<a href="http://images2015.cnblogs.com/blog/73542/201609/73542-20160908160100738-426350238.png"></a>
machine_stop_alarm_msg 表隻有一個聚集索引PK_machine_stop_alarm_msg,字段為recid。
<a href="http://images2015.cnblogs.com/blog/73542/201609/73542-20160908160103144-1808755709.png"></a>
當時我優化的時候,就覺得這個SQL語句存在兩個問題:1 缺少索引; 2 存在隐式轉換問題。當時建立了下面索引,并要求開發人員修改SQL,避免隐式轉換。
在測試環境測試時,我們先不增加這個索引,就出現了下面一個場景,兩者都是走聚集索引掃描:
1: 執行計劃走聚集索引掃描(Cluster Index Scan)
<a href="http://images2015.cnblogs.com/blog/73542/201609/73542-20160908160103957-1011299857.png"></a>
<a href="http://images2015.cnblogs.com/blog/73542/201609/73542-20160908160104894-1168476592.png"></a>
2: 執行計劃走聚集索引掃描(Cluster Index Scan)
<a href="http://images2015.cnblogs.com/blog/73542/201609/73542-20160908160105785-45033768.png"></a>
這裡兩者的執行計劃一樣,這個應該很好了解,缺少相關索引,而且發生隐式轉換的不是索引所在的字段,那麼即使存在隐式轉換,它的執行計劃是一樣的。 這裡沒有太多要解釋的。
那麼我們接下來看看看增加了索引後,兩者的實際執行計劃。
<a href="http://images2015.cnblogs.com/blog/73542/201609/73542-20160908160107691-637654608.png"></a>
<a href="http://images2015.cnblogs.com/blog/73542/201609/73542-20160908160108473-352319167.png"></a>
上面發生隐式轉換的SQL的執行計劃,多了一個常量掃描(Constant Scan),常量掃描做的工作是根據使用者輸入的SQL中的常量生成一個行 ,MSDN的介紹如下:
"The Constant Scan operator introduces one or more constant rows into a query. A Compute Scalar operator is often used after a Constant
Scan to add columns to a row produced by the Constant Scan operator"
常量掃描會引入一個或者多個常量行到一個查詢中;通常情況下緊跟常量掃描的是計算标量運算符,計算标量運算符會為常量掃描運算符産生的行添加列。
<a href="http://images2015.cnblogs.com/blog/73542/201609/73542-20160908160110066-1392044987.png"></a>
如果你想知道執行計劃裡面的Expr1004、 Expr1005、Expr1003對應啥,看看執行計劃就知道了(其中Expr1003為(62),一開始不明其什麼意義,後面咨詢了宋大神,才知道62是個flag,意思是等于号)
<a href="http://images2015.cnblogs.com/blog/73542/201609/73542-20160908160110957-287190179.png"></a>
發生隐式轉換的SQL還多了一個Nested Loop(Inner Join)操作。另外,即使這兩個SQL依然都是索引查找(Index Seek),但是兩種的IO開銷還是有所差別的。
<a href="http://images2015.cnblogs.com/blog/73542/201609/73542-20160908160111863-869724979.png"></a>
<a href="http://images2015.cnblogs.com/blog/73542/201609/73542-20160908160112863-834572165.png"></a>