這里討論的count查詢優化是針對INNODB存儲引擎的!
首先拋出一個問題
count(*)、count(主鍵)、count(1)、count(字段)它們四者之間的效率如何排序?
在我們以往的工作經驗中,總是聽到說不要使用count(*),因為 * 號代表了所有列,計算會更慢,然后就推薦了使用count(主鍵)。但事實真的是這樣嗎?
筆者在最近的學習中也才認識到這個錯誤。
這四者正確的排序應該是這樣的:
count(1) > count(*) > count(主鍵) > count(字段)
筆者特意在一張只有主鍵索引的1千萬條數據的表中做了測試,以下是測試的結果:
這張圖的數據不是偶然的,筆者針對每種查詢執行了多次,結果不會有太大的變化,驗證了以上排序的正確性。
count()的作用
count()是一個特殊的函數,有兩種非常不同的作用:它可以統計某個列值的數量,也可以統計行數。在統計列值時要求列值
是非空的(不統計NULL)。如果在count()的括號中指定了列或者列的表達式,則統計的就是這個表達式有值的結果數,而不是NULL。
count()的另一個作用是統計結果集的行數。當MYSQL確認括號內的表達式值不可能為空時,實際上就是在統計行數。最簡單的就
是當我們使用count(*)的時候,這種情況下通配符 * 並不會像我們猜想的那樣擴展成所有的列,實際上,它會忽略所有的列而直接統計
所有的行數。
最常見的一個錯誤就是,在括號內指定了一個列卻希望統計結果集的行數。如果希望知道的是結果集的行數,最好使用COUNT(*),
這樣寫意義清晰,性能也會很好。
count()的另一個優化
業務代碼中,有這樣一個需求,需要根據一個或多個條件,查詢是否存在記錄,不關心有多少條記錄。普遍的SQL及代碼寫法如下:
SELECT count(*) FROM T WHERE a = 1 AND b = 2
java寫法如下
Integer exist = xxDao.existXxxxByXxx(params); if ( exist != NULL ) { //當存在時,執行這里的代碼 } else { //當不存在時,執行這里的代碼 }
對於此處的select count(*),並不需求將符合條件的條數都統計出來,可以優化成如下的SQL:
SELECT count(*) FROM T WHERE a = 1 AND b = 2 LIMIT 1
這里相比上面的SQL多了LIMIT關鍵字,在MYSQL中,執行器執行到LIMIT關鍵字,只要滿足LIMIT的條數,存儲引擎就不會再對數據文件
進行檢索,直接返回,所以這里只需要檢索到1條之后就返回結果,效率可想而知快了不少!