2017年計(jì)算機(jī)等考三級(jí)數(shù)據(jù)庫輔導(dǎo):SQLServer數(shù)據(jù)庫的相關(guān)性能問題與隱式轉(zhuǎn)換

字號(hào):


     
    

導(dǎo)讀:此文章主要向大家描述的是正確的隱式轉(zhuǎn)換與SQL Server數(shù)據(jù)庫的相關(guān)性能問題,面臨的問題可以對(duì)其通過在HumanResources.Employee表的SQL Server 2005 AdventureWorks數(shù)據(jù)庫中做一個(gè)類似的查詢來看到。
    


    

為了幫助我們更好地理解SQL Server在我們運(yùn)行這些查詢時(shí)都做了什么,讓我們來查找IO統(tǒng)計(jì)數(shù)據(jù),
    


    

我面臨的問題可以通過在HumanResources.Employee表的SQL Server 2005 AdventureWorks數(shù)據(jù)庫中做一個(gè)類似的查詢來看到。為了幫助我們更好地理解SQL Server在我們運(yùn)行這些查詢時(shí)都做了什么,讓我們來查找IO統(tǒng)計(jì)數(shù)據(jù),并且使用SSMS菜單命令Query\Include Actual Execution Plan。
    

為了使用AdventureWorks數(shù)據(jù)庫并且啟用IO統(tǒng)計(jì)數(shù)據(jù),讓我們從下面的查詢開始:
    


    
    

use AdventureWorks   
go  
SET STATISTICS IO ON  
go

    
    

這是對(duì)Employee表的一個(gè)查詢,它類似于給我?guī)砩鲜雎闊┑牟樵儯?BR>    


    
    

SELECT EmployeeID, NationalIDNumber, LoginID   
FROM HumanResources.Employee  
WHERE NationalIDNumber = 112457891 go

    
    

它看起來似乎不會(huì)給我們帶來什么麻煩。HumanResources.Employee表有一個(gè)以NationalIDNumber開始的索引,因此執(zhí)行這個(gè)查詢只是查找112457891的位置然后對(duì)這個(gè)表的行作查找。但是統(tǒng)計(jì)數(shù)據(jù)和查詢計(jì)劃顯示了事情并非如此簡單。這是相關(guān)的信息:
    


    
    

EmployeeID NationalIDNumber LoginID   
4 112457891 adventure-works\rob0  
(1 row(s) affected)  
Table 'Employee'. Scan count 1, logical reads 6,  
physical reads 0, read-ahead reads 0,  
lob logical reads 0, 
lob physical reads 0,  
lob read-ahead reads 0.  
(1 row(s) affected)

    
    

該統(tǒng)計(jì)數(shù)據(jù)顯示有一個(gè)掃描,而這正是問題所在。Adventureworks.HumanResources.Employee只有291行,因此這可能真的可以很快地運(yùn)行并且看似不會(huì)造成什么問題。我使用的表有數(shù)百萬行,表掃描正是兇手,因?yàn)樗鼘?duì)于每次查詢都要花幾秒的時(shí)間。
    

由于NationalIDNumber字段在索引以及該索引字段的開頭,那么為什么這里只有一個(gè)掃描而沒有查找呢?下面的查詢機(jī)壞昂告訴我們?yōu)槭裁础_@是你可以看到索引掃描的整個(gè)計(jì)劃:
    


    

該索引掃描的工具技巧給出所有不同點(diǎn)的詳細(xì)信息,如下所示:
    


    

紅色箭頭指向了該問題。函數(shù)CONVERT_IMPLICIT(int, [AdventureWorks].[HumanResources].[Employee].[NationalIDNumber, 0)在它與我們傳遞到該查詢中的整數(shù)常數(shù)1124579811比較之前修改了NationalIDNumber字段。如果你查看這張表的定義,那么你就會(huì)看到NationalIDNumber被定義成nvarchar(15)。一旦這里有個(gè)函數(shù),即使是我們?cè)谶@里看到的自帶函數(shù)用于這個(gè)字段,SQL Server數(shù)據(jù)庫都不能在NationalDNumber上使用索引,它會(huì)返回到一個(gè)掃描中。
    


    

更改這個(gè)查詢來與一個(gè)字符串常數(shù)比較,這個(gè)問題將得到解決:
    


    


    


    


    

SELECT EmployeeID, NationalIDNumber, LoginID   
FROM HumanResources.Employee  
WHERE NationalIDNumber = '112457891' 
go


    

這些信息現(xiàn)在顯示為零掃描,這正是我們想要的。在這個(gè)案例中,邏輯讀的不同點(diǎn)是只有2,這是因?yàn)镋mployee很小。如果在數(shù)百萬行的表上進(jìn)行這個(gè)過程,那么邏輯讀的不同點(diǎn)將變成幾千。
    


    


    


    


    


    

EmployeeID NationalIDNumber LoginID   
4 112457891 adventure-works\rob0  
(1 row(s) affected)  
Table 'Employee'. Scan count 0, 
logical reads 4, physical reads 0, 
read-ahead reads 0, lob logical reads 0, 
lob physical reads 0, 
lob read-ahead reads 0.  (1 row(s) affected)


    

查詢計(jì)劃顯示查找和主鍵查找正是我們希望看到的。
    

一個(gè)字符串用于存儲(chǔ)一個(gè)數(shù)字型主鍵的問題是很常見的。SQL Server數(shù)據(jù)庫將很忠實(shí)地執(zhí)行隱式轉(zhuǎn)換并返回正確的結(jié)果,但是要以較差的性能為代價(jià),通過一個(gè)掃描而不是查找來進(jìn)行并且要正確使用該索引。
    

要一直警惕隱式轉(zhuǎn)換,尤其是有存儲(chǔ)數(shù)字型鍵的字符串的時(shí)候。
    


    

我甚至在varchar字段與nvarchar字段進(jìn)行比較的時(shí)候看到這個(gè)問題。
    


    

更正這個(gè)問題是很簡單的,你只需要確保在相似的數(shù)據(jù)類型上執(zhí)行比較。