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