淺聊 count(1)、count(*) 與 count(列名) 的區別
簡單來說:
COUNT(1) 和 COUNT(*) 表示的是直接查詢符合條件的數據庫表的行數。而 COUNT(列名) 表示的是查詢符合條件的列的值不為 NULL 的行數。
除了查詢得到結果集有區別之外,在性能方面 COUNT() 約等于 COUNT(1),但是 COUNT() 是 SQL92 定義的標準統計行數的語法**。因為它是標準語法,所以 MySQL 數據庫對其進行了很多優化。
COUNT
關于 COUNT 函數,在 MySQL 官網中有詳細介紹:
COUNT(expr) 返回 SELECT 語句檢索的行中 expr 的值不為 NULL 的數量。結果是一個 BIGINT 值。
如果查詢結果沒有命中任何記錄,則返回 0。
但是,值得注意的是,COUNT(*) 的統計結果中會包含值為 NULL 的行數。
即以下表記錄:
create table #bla(id int,id2 int)
insert #bla values(null,null)
insert #bla values(1,null)
insert #bla values(null,1)
insert #bla values(1,null)
insert #bla values(null,1)
insert #bla values(1,null)
insert #bla values(null,null)
使用語句 count(*),count(id),count(id2)查詢結果如下:
select count(*),count(id),count(id2)
from #bla
results 7 3 2
COUNT(*)的優化
COUNT(*) 在 MySQL 中的優化與所使用的執行引擎密切相關,常見的執行引擎包括 MyISAM 和 InnoDB。
MyISAM 和 InnoDB 之間有許多區別,其中一個關鍵區別與接下來要討論的 COUNT(*) 有關:MyISAM 不支持事務,其鎖定級別為表級鎖;而 InnoDB 支持事務,并且使用行級鎖。
由于 MyISAM 的表級鎖,同一表上的操作需要串行進行。因此,MyISAM 做了一個簡單的優化,即單獨記錄表的總行數。對于不帶 WHERE 條件的 COUNT(*) 查詢,可以直接返回這個記錄的值。
MyISAM 之所以能夠記錄表中的總行數并供 COUNT(*) 查詢使用,是因為其表級鎖機制保證了行數查詢的準確性,沒有并發的行數修改。
然而,對于 InnoDB,這種緩存操作就不可行了,因為 InnoDB 支持事務,其中大部分操作使用行級鎖,可能導致表的行數被并發修改,從而使緩存的行數不準確。
盡管如此,InnoDB 也對 COUNT(*) 語句進行了一些優化。
從 MySQL 8.0.13 開始,針對 InnoDB 的 SELECT COUNT(*) FROM tbl_name 查詢,在掃表過程中進行了優化,前提是查詢語句不包含 WHERE 或 GROUP BY 等條件。
由于 COUNT(*) 只是為了統計總行數,不關心具體值,因此,在掃表過程中選擇成本較低的索引可以節省時間。
InnoDB 中的索引分為聚簇索引(主鍵索引)和非聚簇索引(非主鍵索引)。非聚簇索引相比聚簇索引更小,因此 MySQL 會優先選擇最小的非聚簇索引來掃表。
因此,在建表時,除了主鍵索引外,創建一個非主鍵索引也是有必要的。
這些優化的前提是查詢語句中不包含 WHERE 和 GROUP BY 條件。
COUNT(*)和 COUNT(1)
MySQL 官方文檔對于 COUNT(*)和 COUNT(1) 的性能差異沒有做出具體說明。不過,可以從一些實踐和理論上推斷一些情況。
有些人認為COUNT(*) 在執行時會轉換成 COUNT(1),因此 COUNT(1) 少了轉換步驟,所以更快。這個說法在某些情況下可能是正確的,因為 COUNT(*) 會返回表中所有行的數目,而 COUNT(1) 只需要計算行數而不需要檢查列值。
另一方面,也有人認為 MySQL 針對 COUNT(*) 做了特殊優化,因此 COUNT(*) 更快。這個說法也是有一定道理的,因為 COUNT(*) 是 SQL92 定義的標準語法,MySQL 可能對其進行了一些優化。
綜上所述,對于 COUNT(*) 和 COUNT(1) 的性能差異,可能取決于具體的情況和 MySQL 的版本。在實際情況中,可以根據具體的需求和環境選擇合適的寫法。
InnoDB handles SELECT COUNT(*) and SELECT COUNT(1) operations in the same way. There is no performance difference.
畫重點:same way, no performance difference。所以,對于 COUNT(1) 和 COUNT(*),MySQL 的優化是完全一樣的,根本不存在誰比誰快!
那既然COUNT(*) 和 COUNT(1)一樣,建議用哪個呢?
建議使用 COUNT(*)!因為這個是 SQL92 定義的標準統計行數的語法,而且本文只是基于 MySQL 做了分析,關于 Oracle 中的這個問題,也是眾說紛紜的呢。
COUNT(字段)
最后,就是我們一直還沒提到的 COUNT(字段),他的查詢就比較簡單粗暴了,就是進行全表掃描,然后判斷指定字段的值是不是為 NULL,不為 NULL 則累加。
相比 COUNT(*),COUNT(字段) 多了一個步驟就是判斷所查詢的字段是否為 NULL,所以他的性能要比 COUNT(*) 慢。