最近在學習極客時間丁奇的專欄《MySQL實戰45講》中第14講有關
count
函數的時候以爲這一講頗有意思,遂決定以實操記錄,以加深印象。mysql
在MySQL
中,當咱們須要獲取某張表中的總行數時,通常會選擇使用下面的語句sql
select count(*) from table;
複製代碼
其實count
函數中除了*
還能夠放其餘參數,好比常數、主鍵id
、字段,那麼它們有什麼區別?各自效率如何?咱們應該使用哪一種方式來獲取表的行數呢?markdown
當搞清楚count
函數的運行原理後,相信上面幾個問題的答案就會了然於胸。函數
爲了解決上述的問題,我建立了一張 user
表,它有兩個字段:主鍵id
和name
,後者能夠爲null
,建表語句以下。學習
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主鍵',
`name` varchar(255) DEFAULT NULL COMMENT '姓名',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
複製代碼
在該表中共有6000000條數據,前1000000條數據行的name字段爲空,其他數據行name=id
,使用存儲過程造測試數據的代碼以下測試
-- 使用存儲過程造測試數據
delimiter;;
create procedure idata()
begin
declare i int;
set i=1;
while(i<=6000000)do
insert into user values(i, i);
set i=i+1;
end while;
end;;
delimiter;
call idata();
-- 將前1000000條數據的name字段置爲null
update user set name=null where id<1000000;
複製代碼
爲了區分count
函數不一樣參數的區別,主要從執行時間和掃描行數這兩方面來描述SQL
的執行效率,同時還會從返回結果來描述`count函數的特性。優化
*
符號 —— select count(*) from user;
select count(1) from user;
select count(id) from user;
select count(name) from user;
*
符號mysql> select count(*) from user;
+----------+
| count(*) |
+----------+
| 6000000 |
+----------+
1 row in set (0.76 sec)
複製代碼
遍歷全表,不取值(優化後,一定不是null,不取值),累加計數,最終返回結果。spa
mysql> select count(1) from user;
+----------+
| count(1) |
+----------+
| 6000000 |
+----------+
1 row in set (0.76 sec)
複製代碼
遍歷全表,一行行取數據,將每一行賦值爲1,判斷到該字段不可爲空,累加計數,最終返回結果。code
mysql> select count(id) from user;
+-----------+
| count(id) |
+-----------+
| 6000000 |
+-----------+
1 row in set (0.85 sec)
複製代碼
遍歷全表,一行行取數據(會選擇最小的索引樹來遍歷,因此比相同狀況下的count
字段效率更高),取每行的主鍵id
,判斷到該字段不可爲空,累加計數,最終返回結果。orm
mysql> select count(name) from user;
+-------------+
| count(name) |
+-------------+
| 5900001 |
+-------------+
1 row in set (0.93 sec)
複製代碼
null
,而後再判斷該字段的值是否爲null
,不爲null
才累加計數,最終返回結果。首先從結果集的角度來看,前三條 SQL 語句的目的是同樣的——返回的是全部行數,而 count 函數的參數是普通字段且字段默認爲 null 的時候,它返回的是該字段不爲 null 的行數。
從執行時間上來看的話,效率大體是count(可爲空的字段)
< count(非空字段)
< count(常數)
< count(*)
。
count
是一個聚合函數,對於返回的結果集,一行行地判斷,若是count
函數的參數不是NULL
,累計值就加1,不然不加。最後返回累計值。
count(*)
速度最快的緣由是它不會在計數的時候去取每行數據值count(1)
比count(*)
稍慢的緣由是它會取每一個數據行並賦值爲1count(非空字段)
比count(1)
稍慢的緣由是它會從每一個數據行中取出主鍵 idcount(可爲空的字段)
最慢的緣由是它可能須要判斷每一個數據行中的改字段是否爲 null因此,最好仍是用count(*)
。