1. 程式人生 > >SQL SERVER中隱式轉換的一些細節淺析

SQL SERVER中隱式轉換的一些細節淺析

開發 tle ima 我們 rom spa 都是 date 分享

原文:SQL SERVER中隱式轉換的一些細節淺析

其實這是一篇沒有技術含量的文章,精通SQL優化的請繞道。這個緣起於在優化一個SQL過程中,同事問了我一個問題,為什麽SQL中存在隱式轉換,但是執行計劃沒有變? 我思索了一下,覺得這個問題也有點意思,說不定有些對隱式轉換了解得不深入的同學都有此疑問,那麽下面結合上下文場景做一個細節方面的解答。

我們一個系統中使用了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。

技術分享圖片

當時我優化的時候,就覺得這個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)

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)

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;

技術分享圖片

這裏兩者的執行計劃一樣,這個應該很好理解,缺少相關索引,而且發生隱式轉換的不是索引所在的字段,那麽即使存在隱式轉換,它的執行計劃是一樣的。 這裏沒有太多要解釋的。

那麽我們接下來看看看增加了索引後,兩者的實際執行計劃。

技術分享圖片

技術分享圖片

現在同事糾結的就是即使發生了隱式轉換,為什麽執行計劃還是走索引查找(Index Seek)呢? 其實很多人有一個誤區,SQL Server當中並不是所有的隱式轉換都會導致索引掃描(Index Scan),關於這個請見我這篇博客SQL SERVER中什麽情況會導致索引查找變成索引掃描 。也就是說隱式轉導致索引掃描也是有條件的。這裏不再做展開講,沒有太多意思。另外,我們再來對比一下兩者的執行計劃。

上面發生隱式轉換的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"

常量掃描會引入一個或者多個常量行到一個查詢中;通常情況下緊跟常量掃描的是計算標量運算符,計算標量運算符會為常量掃描運算符產生的行添加列。

技術分享圖片

如果你想知道執行計劃裏面的Expr1004、 Expr1005、Expr1003對應啥,看看執行計劃就知道了(其中Expr1003為(62),一開始不明其什麽意義,後面咨詢了宋大神,才知道62是個flag,意思是等於號)

技術分享圖片

發生隱式轉換的SQL還多了一個Nested Loop(Inner Join)操作。另外,即使這兩個SQL依然都是索引查找(Index Seek),但是兩種的IO開銷還是有所區別的。

技術分享圖片

技術分享圖片

技術分享圖片

技術分享圖片

SQL SERVER中隱式轉換的一些細節淺析