SELECT COUNT(*) FROM table_name是個再常見不過的統計需求了。
本文帶你了解下Mysql的COUNT函數。
一、COUNT函數
關於COUNT函數,在MySQL官網中有詳細介紹:

翻譯一下:
COUNT(expr),返回SELECT語句檢索的行中expr的值不為NULL的數量,結果是一個BIGINT值。- 如果查詢結果沒有命中任何記錄,則返回
0 COUNT(*)的統計結果中,會包含值為NULL的行數。
在《阿里巴巴Java開發手冊》也有如下要求:

二、COUNT(列名)、COUNT(常量)和COUNT(*)
前面我們提到過
COUNT(expr)用於做行數統計,那么COUNT(列名)、COUNT(常量)和COUNT(*)這三種語法中,expr分別是列名、 常量 和*。
2.1 COUNT(*)和COUNT(常量)
在列名、常量和 *這三個條件中,常量是一個固定值,肯定不為NULL。*可以理解為查詢整行,所以肯定也不為NULL,那么就只有列名的查詢結果可能是NULL。
所以, COUNT(常量) 和 COUNT(*)表示的是直接查詢符合條件的數據庫表的行數。而COUNT(列名)表示的是查詢符合條件的列的值不為NULL的行數。
2.2 COUNT(*)和COUNT(1)區別
COUNT(1)就是COUNT(常量),對於這二者到底有沒有區別:
- 有的說
COUNT(*)執行時會轉換成COUNT(1),所以COUNT(1)少了轉換步驟,所以更快。 - 還有的說,因為
MySQL針對COUNT(*)做了特殊優化,所以COUNT(*)更快。
到底哪種說法是對的?看下MySQL官方文檔:
InnoDB handles SELECT COUNT(*) and SELECT COUNT(1) operations in the same way. There is no performance difference.
通過文檔,對於COUNT(1)和COUNT(*),MySQL的優化是完全一樣的,根本不存在誰比誰快!
2.3 COUNT(列名)
相較於前兩者,COUNT(列名)的查詢就比較簡單粗暴了,就是進行全表掃描,然后判斷指定字段的值是不是為NULL,不為NULL則累加。
相比
COUNT(*),COUNT(列名)多了一個步驟就是判斷所查詢的字段是否為NULL,所以他的性能要比COUNT(*)慢。
here和group的條件查詢。
2.4 SQL92
除了查詢得到結果集有區別之外,COUNT(*)相比COUNT(常量) 和 COUNT(列名)來講,COUNT(*)是SQL92定義的標准統計行數的語法,因為他是標准語法,所以MySQL數據庫對他進行過很多優化。
SQL92,是數據庫的一個ANSI/ISO標准。它定義了一種語言(SQL)以及數據庫的行為(事務、隔離級別等)。
2.5 COUNT(*)優化
因為COUNT(*)是SQL92定義的標准統計行數的語法,所以MySQL對其進行了很多優化:
MyISAM中會直接把表的總行數單獨記錄下來供COUNT(*)查詢InnoDB會在掃表的時候選擇最小的索引來降低成本。
這些優化的前提都是沒有進行
where和group的條件查詢,更多請參考MySQL 全表 COUNT(*) 簡述
三、總結
COUNT函數用於統計表行數,按照效率比較的話:
count(*)=count(常量)>count(列名)
3.1 小建議
既然 count(*) 在查詢上依賴於所有的數據集,所以我們在設計上也需要盡量的規避全量 count。
通常情況我們針對可預見的 count 查詢會做適當的緩存,可以是 Redis,也可以是獨立的 MySQL count 表。
