一:Mysql中的Count的概念:
COUNT()函數返回表中的行數。 COUNT()函數允許我們對錶中符合特定條件的所有行進行計數。
二:場景:
1:在不同的搜索引擎中,計數統計的實現方式是不一樣的;
MyISAM 引擎,比較簡單粗暴,直接將表的總行數存儲在磁盤上,因此效率很高;
InnoDB 引擎中,執行時,需要一行行的把數據查出來,然後累加;
2:場景說明:
A: count(*)包括了所有的列,相當於行數,在統計結果的時候,不會忽略列值爲NULL
並不會把全部字段取出來,而是專門做了優化,不取值,count(*)肯定不是null,按行累加
B.: count(1)包括了忽略所有列,用1代表代碼行,在統計結果的時候,不會忽略列值爲NULL
innodb引擎遍歷整張表,但不取值,返回給server層,server對於返回的每一行,放一個數字1進去,判斷是不可能爲空的,就按行累加
C: count(列名)只包括列名那一列,在統計結果的時候,會忽略列值爲空(這裏的空不是隻空字符串或者0,而是表示null)的計數,即某個字段值爲NULL時,不統計。
如果這個字段定義爲not null,一行行的從記錄裏面讀出這個字段,判斷不爲空,則累加值
如果這個字段定義允許爲null,那麼執行的時候,判斷到有可能爲null,還要把值取出來在判斷一下,不是null才累加
D: count(主鍵)
innodb引擎會遍歷整張表,把每一行的id值都取出來,返回給server層,server層判斷id值不爲空,就按行累加
三:性能說明
在 MySQL 5.7 Reference Manual 的官方手冊中:
https://dev.mysql.com/doc/refman/5.7/en/group-by-functions.html#function_count有這麼一段:
InnoDB handles SELECT COUNT(*) and SELECT COUNT(1) operations in the
same way. There is no performance difference.翻譯: InnoDB以相同的方式處理SELECT COUNT(*)和SELECT COUNT(1)操作。沒有性能差異。
所以按照效率排序的話,count(字段)<count(主鍵id)<count(1)≈count(*)
================着重描述一下count(*)與count(1)的區別:
COUNT(1)比COUNT()快,給出的理由是COUNT()會帶來全表掃描。而實際上兩者在高版本(5.5之後)的mysql中並沒有區別。
在MySQL5.7.23.0以後的版本,會默認對主鍵添加索引,所以結論是:
若表中有索引,COUNT()與COUNT(1)均會使用索引。由於MySQL默認對主鍵添加索引,所以對存在主鍵的表進行COUNT()、COUNT(1)查詢也都會使用主鍵索引。即兩者並沒有區別。
COUNT()有兩個非常不同的作用:它可以統計某個列值的數量,也可以統計行數。在統計列值時要求列值是非空的(不統計NULL)。如果在COUNT()的括號中定了列或者列表達式,則統計的就是這個表達式有值的結果數。…COUNT()的另一個作用是統計結果集的行數。當MySQL確認括號內的表達式值不可能爲空時,實際上就是在統計行數。最簡單的就是當我們使用COUNT()的時候,這種情況下通配符並不像我們猜想的那樣擴展成所有的列,實際上,他會忽略所有列而直接統計所有的行數“——《高性能MySQL》。
四:使用的時候的注意事項
在做計數統計的時候
count()、count(常量)和count(列名)的區別:
在《阿里巴巴Java開發手冊》中,強制建議不能使用count(1)和count(列名)來代替count(),那這是爲什麼呢?它們之間的區別又在哪呢?
分析以及原理:
目前基於磁盤的數據庫或者搜索引擎(比如Lucene)的性能瓶頸主要都是在IO階段,相比於CPU和RAM,IO操作實在太慢了,所以這類系統的優化方向也都都是類似的——盡一切可能減少IO的次數(所以很多用ES的程序在性能優化到極限的時候選擇直接上SSD)。這裏統計行數的操作,查詢優化器的優化方向就是選擇能夠讓IO次數最少的索引,也就是基於佔用空間最小的字段所建的索引(每次IO讀取的數據量是固定的,索引佔用的空間越小所需的IO次數也就越少)。而Innodb的主鍵索引是聚簇索引(包含了KEY,除了KEY之外的其他字段值,事務ID和MVCC回滾指針)所以主鍵索引一定會比二級索引(包含KEY和對應的主鍵ID)大,也就是說在有二級索引的情況下,一般COUNT()都不會通過主鍵索引來統計行數,在有多個二級索引的情況下選擇佔用空間最小的。
如果說有張Innodb的表只有主鍵索引,而且記錄還比較大(比如30K),則統計行的操作會非常慢,因爲IO次數會很多
- List item
所以,在對count進行優化的時候,我們可以考慮建一個比較小的二級索引; ALTER TABLE test1 ADD INDEX (status);
如果我們不要求獲取精確的總數的話,可以考慮使用explain獲取行數
關於explain
關於explain,使用mysql的都知道,這個函數是專門用於查看sql語句的執行效率的,網上可供參考的文章很多。
定義: explain 命令速度很快,因爲 explain 用並不真正執行查詢,而是查詢優化器【估算】的行數。
我們使用explain之後,會看到返回很多參數,其中:
rows:顯示MySQL認爲它執行查詢時必須檢查的行數。就是這個東西了,既然我們要獲取的是數據表的行數,那麼可以使用:
explain select * from table;
2、關於返回值
explain函數是會返回一個數組。這樣我們就能通過這個數組獲取到我們需求的rows。
這裏直接獲取這個值即可。速度極快。原來查詢速度是2.33s,換成只用explain之後,速度僅爲0008s,提升十分巨大
============================= 在老版本的mysql 數據庫中
l 列名爲主鍵,count(列名)會比count(1)快
l 列名不爲主鍵,count(1)會比count(列名)快
l 如果表多個列並且沒有主鍵,則 count(1) 的執行效率優於 count(*)
l 如果有主鍵,則 select count(主鍵)的執行效率是最優的
l 如果表只有一個字段,則 select count(*)最優。