COUNT 是數據庫人用凌波微步也躲不開的一道坎。
關於COUNT的使用,有很多不知道為什麼的結論,比如:
- SQL必須用 COUNT(列),不準用COUNT(*),COUNT(*)比COUNT(列)慢?
- 在使用COUNT的時候要用COUNT(1)而不要用COUNT(*),因為使用COUNT(*)的時候會對所有的列進行掃描,相比而言COUNT(1)不用掃描所有列,所以COUNT(1)要快一些?
這些讓人深信不疑,奉為圭臬的結論,到底正不正確?對我們的工作來說,究竟應該在哪種場景下使用哪個?
實驗結論是:
- COUNT(1)和COUNT(*) 並沒有區別
- COUNT(*) 和 COUNT(列) 在沒有索引的情況下一樣快
- 在有索引的情況下,如果該列允許為空,是COUNT(列)快,如果不允許為空,是一樣快的。
下面就用5分鐘來具體講講原因。
一、COUNT是什麼?
COUNT()函數返回表中的行數。COUNT()函數允許您對錶中符合特定條件的所有行進行計數。
COUNT函數有幾種形式:
COUNT(*)、COUNT(column)、COUNT(1)、COUNT(DISTINCT column)和COUNT(expression)。
語法:
1.COUNT(*) 函數返回表中的記錄數:
SELECT COUNT(*) FROM table_name
2.COUNT(column_name) 函數返回指定列的值的數目(NULL 不計入):
SELECT COUNT(column_name) FROM table_name
3. COUNT(DISTINCT column_name) 函數返回指定列的不同值的數目:
SELECT COUNT(DISTINCT column_name) FROM table_name
PS: 這裡的COUNT(1)中的“1”並不表示表中的第一列,它其實是一個表達式,可以換成任意數字或字符或表達式。
二、COUNT(*)與COUNT(列)的區別
要通透的瞭解一個知識點,首先需要了解它的本質。
- COUNT(*)是對全表掃描的統計,不會忽略NULL值,數據庫在處理COUNT(*)的時候只需要找到屬於表的數據塊塊頭,然後計算一下行數就行了,而不用去讀取裡面數據列的數據。
- COUNT(列)是針對於某一列的,如果此列值為空的話,COUNT(列)是不會統計這一行的,所以為了去除列中包含的NULL行,數據庫必須讀取該列的每一行的值,然後確認下是否為NULL,然後在進行計數。
所以兩者根本沒有可比性,性能比較首先要考慮寫法等價,這兩個語句根本就不等價。也就失去了去比較的意義。
兩者無關的話,什麼場景下用哪個呢?
- 在統計表全部行數的時候,COUNT(*)是最佳選擇;
- COUNT(列)對應的列字段如果建了索引,且索引列允許空值,則COUNT(列)會走索引,此時選擇COUNT(列)。
PS: COUNT(列)如果列字段越往後,則訪問的開銷越大,執行速度越慢,所以常用的列要放在靠前的位置,但是COUNT(*)並不受此限制。
三、COUNT(*)與COUNT(1)的區別
上文提到的COUNT(1)比COUNT(*)快,給出的理由是COUNT(*)會帶來全表掃描。而實際上兩者並沒有區別。
在MySQL5.7.23.0以後的版本,會默認對主鍵添加索引,所以結論是:
若表中有索引,COUNT(*)與COUNT(1)均會使用索引。由於MySQL默認對主鍵添加索引,所以對存在主鍵的表進行COUNT(*)、COUNT(1)查詢也都會使用主鍵索引。即兩者並沒有區別。
最後,給大家分享暢銷書作家成甲的一句話,“那些能夠改變你行動的信息,我們稱之為知識”,希望本文帶給大家的是知識而不只是信息~
若有不完善的地方,歡迎大家指正~
閱讀更多 老王談運維 的文章