1. 程式人生 > >關於select Count()的使用和性能問題

關於select Count()的使用和性能問題

name row 最大的 使用 strong small margin alt 建立

比如Count(*) FROM E_Table WHERE [date] > ‘2008-1-1‘ AND istrue = 0

由於操作的數據比較大(400萬以上),所以使用了兩個數據庫,一個用於更新,執行頻繁的Insert、Update操作,把索引建在了主鍵id上,另一個數據庫定時復制前一個數據庫的數據,用於檢索查詢,在[date]字段上建立了聚簇索引,在[istrue]字段上建立了非聚簇索引。這樣下來,每次Count花費不超過2s的時間。

後來又復制了一份數據庫,做了一些字段的調整,其中istrue字段被刪除,非聚簇索引也去除了,而[date]字段和聚簇索引沒有變動。

結果在執行Count(*) FROM E_Table WHERE [date] > ‘2008-1-1‘的時候,卻出現了奇怪的現象,原本以為會更快完成的檢索(因為表的條數有減少),反而花費了30+s。

反復檢查兩個表,沒有發現除了非聚簇索引外的不同,又測試在前一個表上執行相同的Count(*) FROM E_Table WHERE [date] > ‘2008-1-1‘,花費時間在1s左右。

只好把非聚簇索引和istrue字段加上,結果檢索速度立刻回到1s!

一個在檢索條件中不出現的字段為什麽會影響檢索的效率呢?仔細對比兩次的Excuting Plan,終於發現了問題所在:

花費30s的檢索第一步:

技術分享

花費1s的檢索第一步:

技術分享

除了兩者的操作不同(一般情況下,Clustered Index Seek的速度應該快於Index Scan),CPU Cost是近似的,Number of Rows是一樣的,最大的差別在I/O Cost上,前者是後者的5倍!

究其原因,主要是因為SQL Server執行Count操作的策略。當建有索引時,而且Count的參數是*或者Not Null類型的字段時,SQL Server會對所有索引列中體積最小的索引進行Scan。而istrue字段是bit類型的,只占1個字節,[date]字段是smalldatetime類型的,占4個字節。檢索前者的I/O自然遠遠小於檢索後者的,在同樣的內存條件下,通過前者檢索花費的時間也要遠小於後者。

當然,如果Count的參數是某個Null類型的字段,SQL Server則只能對該字段進行Scan,因為這時該字段為Null的記錄不記錄的。所以,一個優化建議是,盡量少用Null類型字段,使用默認值+bit類型的索引字段會提高Count檢索的效率。

另外:關於where條件

COUNT時的WHERE

簡單說下,就是COUNT的時候,如果沒有WHERE限制的話,MySQL直接返回保存有總的行數
而在有WHERE限制的情況下,總是需要對MySQL進行全表遍歷。

優化總結
1.任何情況下SELECT COUNT(*) FROM tablename是最優選擇;
2.盡量減少SELECT COUNT(*) FROM tablename WHERE COL = ‘value’ 這種查詢;
3.杜絕SELECT COUNT(COL) FROM tablename WHERE COL2 = ‘value’ 的出現。

關於select Count()的使用和性能問題