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


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

我們一個系統中使用了ORMLite框架,粗心的開發人員弄出了不少下面這樣的SQL語句,都存在隱式轉換問題,如下所示,表machine_stop_alarm_msg 的結構如下,字段machine_no、status都為VARCHAR(10),但是下面SQL,傳入的變量@P0,@P1都是NVARCHAR(4000)類型。

 

clipboard

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。

clipboard

 

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

clipboard

clipboard

 

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;

clipboard

 

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

那么我們接下來看看看增加了索引后,兩者的實際執行計划。

 

clipboard

clipboard

 

現在同事糾結的就是即使發生了隱式轉換,為什么執行計划還是走索引查找(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"

 

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

clipboard

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

clipboard

 

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

clipboard

 

image

 

 

clipboard

 

image


免責聲明!

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



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