count(列名)某個字段值爲NULL時,不統計程序員
若是問一個程序員MySQL中SELECT COUNT(1)和SELECT COUNT(*)有什麼區別,會有不少人給出這樣的答案「SELECT COUNT(*)」最終會轉化成「SELECT COUNT(1),而SELECT COUNT(1)省略了轉換的這一步,因此SELECT COUNT(1)效率更高「,甚至有一些面試官也會給出相似的答案。最近在看一些歷史遺留代碼,絕大多數統計數量的SQL都在用SELECT COUNT(1),以爲有必要搞清楚這個問題。面試
首先,以咱們最多見的兩種數據庫表引擎MyISAM和Innodb來說。數據庫
MyISAM在統計表的總行數的時候會很快,可是有個大前提,不能加有任何WHERE條件。這是由於:MyISAM對於表的行數作了優化,具體作法是有一個變量存儲了表的行數,若是查詢條件沒有WHERE條件則是查詢表中一共有多少條數據,MyISAM能夠作到迅速返回,因此也解釋了若是加WHERE條件,則該優化就不起做用了。細心的同窗會發現,innodb的表也有這麼一個存儲了錶行數的變量,可是很遺憾這個值是一個估計值,沒有什麼實際意義。性能優化
在該引擎下,COUNT(1)和COUNT(*)哪一個快呢?結論是:這倆在高版本的MySQL(5.5及之後,5.1的沒有考證)是沒有什麼區別的,也就沒有COUN(1)會比COUNT(*)更快這一說了。函數
WHY?這就要從COUNT()函數的具體含義提及了。」性能
COUNT()有兩個很是不一樣的做用:它能夠統計某個列值的數量,也能夠統計行數。在統計列值時要求列值是非空的(不統計NULL)。若是在COUNT()的括號中定了列或者列表達式,則統計的就是這個表達式有值的結果數。......COUNT()的另外一個做用是統計結果集的行數。當MySQL確認括號內的表達式值不可能爲空時,實際上就是在統計行數。最簡單的就是當咱們使用COUNT(*)的時候,這種狀況下通配符*並不像咱們猜測的那樣擴展成全部的列,實際上,他會忽略全部列而直接統計全部的行數「——《高性能MySQL》。
一般,咱們將第一個字段(通常是ID)做爲主鍵,那麼這個時候COUNT(1)實際統計的就是行數(此處表達有誤,詳見文章結尾),由於主鍵確定是非NULL的。問題是Innodb是經過主鍵索引來統計行數的嗎?結論是:若是該表只有一個主鍵索引,沒有任何二級索引的狀況下,那麼COUNT(*)和COUNT(1)都是經過經過主鍵索引來統計行數的。若是該表有二級索引,則COUNT(1)和COUNT(*)都會經過佔用空間最小的字段的二級索引進行統計,也就是說雖然COUNT(1)指定了第一列(此處表達有誤,詳見文章結尾)可是innodb不會真的去統計主鍵索引(通常爲第一個字段的索引)。測試
新建一張基於Innodb的表,只有一個ID主鍵,並插入5w的測試數據,建表語句以下:優化
CREATE TABLE `tb_news` ( `id` bigint(21) NOT NULL AUTO_INCREMENT, `title` varchar(50) NOT NULL, `content` mediumtext NOT NULL, `count_ass` char(1) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=50001 DEFAULT CHARSET=utf8
這個時候執行COUNT(1)和COUNT(*)能夠看到解釋器的結果以下(二者一致,因此就只截了一張圖),能夠看到,二者都用了主鍵索引進行行數的統計:搜索引擎
新建一個二級索引title,以後在分別看一下COUNT(1)和COUNT(*)的解釋器結果(二者依然徹底一致),這時已經用二級索引進行統計而非主鍵索引:spa
在咱們以前特意預留的一個小字段count_ass字段建一個索引,到這一步目前表中有三個索引:一個主鍵索引,兩個二級索引。
這時候咱們再看一下COUNT(1)和COUNT(*)會經過哪一個索引來統計行數(二者仍是一致)。
目前基於磁盤的數據庫或者搜索引擎(好比Lucene)的性能瓶頸主要都是在IO階段,相比於CPU和RAM,IO操做實在太慢了,因此這類系統的優化方向也都都是相似的——盡一切可能減小IO的次數(因此不少用ES的程序在性能優化到極限的時候選擇直接上SSD)。這裏統計行數的操做,查詢優化器的優化方向就是選擇可以讓IO次數最少的索引,也就是基於佔用空間最小的字段所建的索引(每次IO讀取的數據量是固定的,索引佔用的空間越小所需的IO次數也就越少)。而Innodb的主鍵索引是聚簇索引(包含了KEY,除了KEY以外的其餘字段值,事務ID和MVCC回滾指針)因此主鍵索引必定會比二級索引(包含KEY和對應的主鍵ID)大,也就是說在有二級索引的狀況下,通常COUNT()都不會經過主鍵索引來統計行數,在有多個二級索引的狀況下選擇佔用空間最小的。
若是說有張Innodb的表只有主鍵索引,並且記錄還比較大(好比30K),則統計行的操做會很是慢,由於IO次數會不少(這裏就不作實驗截圖了,有興趣能夠本身試一下)。
一個優化方案就是預先建一個小字段並建二級索引專門用來統計行數,極端狀況下這種優化速度提升上千倍也是正常的。
結論就是對於COUNT(1)和COUNT(*)執行優化器的優化是徹底同樣的,並無COUNT(1)會比COUNT(*)快這個說法。