需要注意的是,下面的討論還是基於 InnoDB 引擎的。
這里,首先你要弄清楚 count() 的語義。count() 是一個聚合函數,對於返回的結果集,一行行地判斷,如果 count 函數的參數不是 NULL,累計值就加 1,否則不加。最后返回累計值。
所以,count(*)、count(主鍵 id) 和 count(1) 都表示返回滿足條件的結果集的總行數;而 count(字段),則表示返回滿足條件的數據行里面,參數“字段”不為 NULL 的總個數。
至於分析性能差別的時候,你可以記住這么幾個原則:
- server 層要什么就給什么;
- InnoDB 只給必要的值;
- 現在的優化器只優化了 count(*) 的語義為“取行數”,其他“顯而易見”的優化並沒有做。
對於 count(主鍵 id) 來說,InnoDB 引擎會遍歷整張表,把每一行的 id 值都取出來,返回給 server 層。server 層拿到 id 后,判斷是不可能為空的,就按行累加。
對於 count(1) 來說,InnoDB 引擎遍歷整張表,但不取值。server 層對於返回的每一行,放一個數字“1”進去,判斷是不可能為空的,按行累加。
單看這兩個用法的差別的話,你能對比出來,count(1) 執行得要比 count(主鍵 id) 快。因為從引擎返回 id 會涉及到解析數據行,以及拷貝字段值的操作。
對於 count(字段) 來說:
- 如果這個“字段”是定義為 not null 的話,一行行地從記錄里面讀出這個字段,判斷不能為 null,按行累加;
- 如果這個“字段”定義允許為 null,那么執行的時候,判斷到有可能是 null,還要把值取出來再判斷一下,不是 null 才累加。
- 也就是前面的第一條原則,server 層要什么字段,InnoDB 就返回什么字段。
但是 count(*) 是例外,並不會把全部字段取出來,而是專門做了優化,不取值。count(*) 肯定不是 null,按行累加。
看到這里,你一定會說,優化器就不能自己判斷一下嗎,主鍵 id 肯定非空啊,為什么不能按照 count(*) 來處理,多么簡單的優化啊。
當然,MySQL 專門針對這個語句進行優化,也不是不可以。但是這種需要專門優化的情況太多了,而且 MySQL 已經優化過 count(*) 了,你直接使用這種用法就可以了。
所以結論是:按照效率排序的話,count(字段)<count(主鍵 id)<count(1)≈count(*),所以我建議你,盡量使用 count(*)。
轉載請注明出處!