其實這是一篇沒有技術含量的文章,精通SQL優化的請繞道。這個緣起於在優化一個SQL過程當中,同事問了我一個問題,爲何SQL中存在隱式轉換,可是執行計劃沒有變? 我思索了一下,以爲這個問題也有點意思,說不定有些對隱式轉換了解得不深刻的同窗都有此疑問,那麼下面結合上下文場景作一個細節方面的解答。 html
咱們一個系統中使用了ORMLite框架,粗心的開發人員弄出了很多下面這樣的SQL語句,都存在隱式轉換問題,以下所示,表machine_stop_alarm_msg 的結構以下,字段machine_no、status都爲VARCHAR(10),可是下面SQL,傳入的變量@P0,@P1都是NVARCHAR(4000)類型。 框架
DECLARE @P0 nvarchar(4000),@P1 nvarchar(4000);
SET @P0='1';
SET @P1='K172';
SELECT [recid],[machine_no]
,[stop_stime]
,[stop_etime]
,[status]
,[memo]
,[createddate]
FROM machine_stop_alarm_msg t
WHERE 1=1
AND t.status=@P0
AND t.machine_no in(@P1 )
ORDER BY machine_no,
stop_stime ;
machine_stop_alarm_msg 表只有一個彙集索引PK_machine_stop_alarm_msg,字段爲recid。 oop
當時我優化的時候,就以爲這個SQL語句存在兩個問題:1 缺乏索引; 2 存在隱式轉換問題。當時建立了下面索引,並要求開發人員修改SQL,避免隱式轉換。測試
CREATE NONCLUSTERED INDEX ix_machine_stop_alarm_msg_n1
ON [dbo].[machine_stop_alarm_msg] ([machine_no],[status])
INCLUDE ([recid],[stop_stime],[stop_etime],[memo],[createddate])
GO
在測試環境測試時,咱們先不增長這個索引,就出現了下面一個場景,二者都是走彙集索引掃描: 優化
1: 執行計劃走彙集索引掃描(Cluster Index Scan)spa
SET STATISTICS IO ON;
SET STATISTICS TIME ON;
DECLARE @P0 nvarchar(4000),@P1 nvarchar(4000);
SET @P0='1';
SET @P1='K172';
SELECT [recid],[machine_no]
,[stop_stime]
,[stop_etime]
,[status]
,[memo]
,[createddate]
FROM machine_stop_alarm_msg t
WHERE 1=1
AND t.status=@P0
AND t.machine_no in(@P1 )
ORDER BY machine_no,
stop_stime ;
SET STATISTICS IO OFF;
SET STATISTICS TIME OFF;
2: 執行計劃走彙集索引掃描(Cluster Index Scan) 3d
SET STATISTICS IO ON;
SET STATISTICS TIME ON;
DECLARE @P0 VARCHAR(10),@P1 VARCHAR(10);
SET @P0='1';
SET @P1='K172';
SELECT [recid],[machine_no]
,[stop_stime]
,[stop_etime]
,[status]
,[memo]
,[createddate]
FROM machine_stop_alarm_msg t
WHERE 1=1
AND t.status=@P0
AND t.machine_no in(@P1 )
ORDER BY machine_no,
stop_stime ;
SET STATISTICS IO OFF;
SET STATISTICS TIME OFF;
這裏二者的執行計劃同樣,這個應該很好理解,缺乏相關索引,並且發生隱式轉換的不是索引所在的字段,那麼即便存在隱式轉換,它的執行計劃是同樣的。 這裏沒有太多要解釋的。 code
那麼咱們接下來看看看增長了索引後,二者的實際執行計劃。 htm
如今同事糾結的就是即便發生了隱式轉換,爲何執行計劃仍是走索引查找(Index Seek)呢? 其實不少人有一個誤區,SQL Server當中並非全部的隱式轉換都會致使索引掃描(Index Scan),關於這個請見我這篇博客SQL SERVER中什麼狀況會致使索引查找變成索引掃描 。也就是說隱式轉致使索引掃描也是有條件的。這裏再也不作展開講,沒有太多意思。另外,咱們再來對比一下二者的執行計劃。 blog
上面發生隱式轉換的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"
常量掃描會引入一個或者多個常量行到一個查詢中;一般狀況下緊跟常量掃描的是計算標量運算符,計算標量運算符會爲常量掃描運算符產生的行添加列。
若是你想知道執行計劃裏面的Expr100四、 Expr100五、Expr1003對應啥,看看執行計劃就知道了(其中Expr1003爲(62),一開始不明其什麼意義,後面諮詢了宋大神,才知道62是個flag,意思是等於號)
發生隱式轉換的SQL還多了一個Nested Loop(Inner Join)操做。另外,即便這兩個SQL依然都是索引查找(Index Seek),可是兩種的IO開銷仍是有所區別的。