COUNT(*)/COUNT(id)/COUNT(1)适用场景
1.前言
在使用任何数据库的时候,我们经常会用到类似以下三种方式获得tablename表的行数:
SELECT COUNT(*) FROM tablename
SELECT COUNT(id) FROM tablename(id为自增主键)
SELECT COUNT(1) FROM tablename
大家也会争论到底是哪种方式性能更好,每个人都有自己的一套理论。本着任何结论都有证可依的原则。本次针对这个话题进行研究和测试。
2.测试环境
本章节描述以下内容基于哪些测试环境
2.1.硬件环境
测试统一使用一台vmware虚拟机。
CPU:Intel(R) Core(TM) i7-4712MQ CPU @ 2.30GHz *4
内存:4GB
硬盘:20GB,统一使用ext4文件系统。文件系统使用默认选项。
2.2.软件环境
操作系统:CentOS 6.6 64位
数据库软件: Percona-Server-server-56-5.6.25-rel73.1.el6.x86_64
2.3.数据表
数据库macrodb中存在三张表test1、test2、test3
这三张表的建表ddl语句是:
CREATE TABLE `test1` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`sname` varchar(255) DEFAULT NULL,
`add_timestamp` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_tst1_addtimestamp` (`add_timestamp`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='test1测试表'
CREATE TABLE `test2` (
`id` bigint(20) DEFAULT NULL,
`sname` varchar(255) DEFAULT NULL,
`add_timestamp` datetime DEFAULT NULL,
KEY `idx_tst2_id` (`id`) USING BTREE,
KEY `idx_tst2_adatetime` (`add_timestamp`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='test2测试表没有主键'
CREATE TABLE `test3` (
`id` bigint(20) DEFAULT NULL,
`sname` varchar(255) DEFAULT NULL,
`add_timestamp` datetime DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='test3没有任何索引'
循环往这三张表插入1KW条记录,其中每个表都会有10条记录add_timestamp出现NULL值。
3.测试内容:
3.1.在test1上测试
3.1.1测试COUNT(1)
--------------
explain extended select count(1) from macrodb.test1
--------------
+----+-------------+-------+-------+---------------+-----------------------+---------+------+---------+----------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+-------+---------------+-----------------------+---------+------+---------+----------+-------------+
| 1 | SIMPLE | test1 | index | NULL | idx_tst1_addtimestamp | 6 | NULL | 3363672 | 100.00 | Using index |
+----+-------------+-------+-------+---------------+-----------------------+---------+------+---------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
--------------
show warnings
--------------
+-------+------+---------------------------------------------------------------------+
| Level | Code | Message |
+-------+------+---------------------------------------------------------------------+
| Note | 1003 | /* select#1 */ select count(1) AS `count(1)` from `macrodb`.`test1` |
+-------+------+---------------------------------------------------------------------+
1 row in set (0.00 sec)
--------------
select count(1) from macrodb.test1
--------------
+----------+
| count(1) |
+----------+
| 3287480 |
+----------+
1 row in set (0.46 sec)
3.1.2.测试COUNT(id)
--------------
explain extended select count(id) from macrodb.test1
--------------
+----+-------------+-------+-------+---------------+-----------------------+---------+------+---------+----------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+-------+---------------+-----------------------+---------+------+---------+----------+-------------+
| 1 | SIMPLE | test1 | index | NULL | idx_tst1_addtimestamp | 6 | NULL | 3363672 | 100.00 | Using index |
+----+-------------+-------+-------+---------------+-----------------------+---------+------+---------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
--------------
show warnings
--------------
+-------+------+-------------------------------------------------------------------------------------------+
| Level | Code | Message |
+-------+------+-------------------------------------------------------------------------------------------+
| Note | 1003 | /* select#1 */ select count(`macrodb`.`test1`.`id`) AS `count(id)` from `macrodb`.`test1` |
+-------+------+-------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
--------------
select count(id) from macrodb.test1
--------------
+-----------+
| count(id) |
+-----------+
| 3287480 |
+-----------+
1 row in set (0.54 sec)
3.1.3.测试test1非组件列
--------------
explain extended select count(add_timestamp) from macrodb.test1
--------------
+----+-------------+-------+-------+---------------+-----------------------+---------+------+---------+----------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+-------+---------------+-----------------------+---------+------+---------+----------+-------------+
| 1 | SIMPLE | test1 | index | NULL | idx_tst1_addtimestamp | 6 | NULL | 3363672 | 100.00 | Using index |
+----+-------------+-------+-------+---------------+-----------------------+---------+------+---------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
--------------
show warnings
--------------
+-------+------+-----------------------------------------------------------------------------------------------------------------+
| Level | Code | Message |
+-------+------+-----------------------------------------------------------------------------------------------------------------+
| Note | 1003 | /* select#1 */ select count(`macrodb`.`test1`.`add_timestamp`) AS `count(add_timestamp)` from `macrodb`.`test1` |
+-------+------+-----------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
--------------
select count(add_timestamp) from macrodb.test1
--------------
+----------------------+
| count(add_timestamp) |
+----------------------+
| 3287470 |
+----------------------+
1 row in set (0.58 sec)
3.1.4.测试COUNT(*)
--------------
explain extended select count(*) from macrodb.test1
--------------
+----+-------------+-------+-------+---------------+-----------------------+---------+------+---------+----------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+-------+---------------+-----------------------+---------+------+---------+----------+-------------+
| 1 | SIMPLE | test1 | index | NULL | idx_tst1_addtimestamp | 6 | NULL | 3363672 | 100.00 | Using index |
+----+-------------+-------+-------+---------------+-----------------------+---------+------+---------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
--------------
show warnings
--------------
+-------+------+---------------------------------------------------------------------+
| Level | Code | Message |
+-------+------+---------------------------------------------------------------------+
| Note | 1003 | /* select#1 */ select count(0) AS `count(*)` from `macrodb`.`test1` |
+-------+------+---------------------------------------------------------------------+
1 row in set (0.00 sec)
--------------
select count(*) from macrodb.test1
--------------
+----------+
| count(*) |
+----------+
| 3287480 |
+----------+
1 row in set (0.46 sec)
3.1.5.测试带WHERE的COUNT(*)
--------------
explain extended select count(*) from macrodb.test1 where add_timestamp >'2014-01-01 00:00:00'
--------------
+----+-------------+-------+-------+-----------------------+-----------------------+---------+------+---------+----------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+-------+-----------------------+-----------------------+---------+------+---------+----------+--------------------------+
| 1 | SIMPLE | test1 | range | idx_tst1_addtimestamp | idx_tst1_addtimestamp | 6 | NULL | 1681836 | 100.00 | Using where; Using index |
+----+-------------+-------+-------+-----------------------+-----------------------+---------+------+---------+----------+--------------------------+
1 row in set, 1 warning (0.00 sec)
--------------
show warnings
--------------
+-------+------+---------------------------------------------------------------------------------------------------------------------------------------+
| Level | Code | Message |
+-------+------+---------------------------------------------------------------------------------------------------------------------------------------+
| Note | 1003 | /* select#1 */ select count(0) AS `count(*)` from `macrodb`.`test1` where (`macrodb`.`test1`.`add_timestamp` > '2014-01-01 00:00:00') |
+-------+------+---------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
--------------
select count(*) from macrodb.test1 where add_timestamp>'2014-01-01 00:00:00'
--------------
+----------+
| count(*) |
+----------+
| 3287470 |
+----------+
1 row in set (0.77 sec)
3.1.6.测试带WHERE的COUNT(主键列)
--------------
explain extended select count(id) from macrodb.test1 where add_timestamp >'2014-01-01 00:00:00'
--------------
+----+-------------+-------+-------+-----------------------+-----------------------+---------+------+---------+----------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+-------+-----------------------+-----------------------+---------+------+---------+----------+--------------------------+
| 1 | SIMPLE | test1 | range | idx_tst1_addtimestamp | idx_tst1_addtimestamp | 6 | NULL | 1681836 | 100.00 | Using where; Using index |
+----+-------------+-------+-------+-----------------------+-----------------------+---------+------+---------+----------+--------------------------+
1 row in set, 1 warning (0.00 sec)
--------------
show warnings
--------------
+-------+------+-------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Level | Code | Message |
+-------+------+-------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Note | 1003 | /* select#1 */ select count(`macrodb`.`test1`.`id`) AS `count(id)` from `macrodb`.`test1` where (`macrodb`.`test1`.`add_timestamp` > '2014-01-01 00:00:00') |
+-------+------+-------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
--------------
select count(id) from macrodb.test1 where add_timestamp >'2014-01-01 00:00:00'
--------------
+-----------+
| count(id) |
+-----------+
| 3287470 |
+-----------+
1 row in set (0.86 sec)
3.1.7.测试带WHERE的COUNT(非主键列)
--------------
explain extended select count(add_timestamp) from macrodb.test1 where add_timestamp >'2014-01-01 00:00:00'
--------------
+----+-------------+-------+-------+-----------------------+-----------------------+---------+------+---------+----------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+-------+-----------------------+-----------------------+---------+------+---------+----------+--------------------------+
| 1 | SIMPLE | test1 | range | idx_tst1_addtimestamp | idx_tst1_addtimestamp | 6 | NULL | 1681836 | 100.00 | Using where; Using index |
+----+-------------+-------+-------+-----------------------+-----------------------+---------+------+---------+----------+--------------------------+
1 row in set, 1 warning (0.00 sec)
--------------
show warnings
--------------
+-------+------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Level | Code | Message |
+-------+------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Note | 1003 | /* select#1 */ select count(`macrodb`.`test1`.`add_timestamp`) AS `count(add_timestamp)` from `macrodb`.`test1` where (`macrodb`.`test1`.`add_timestamp` > '2014-01-01 00:00:00') |
+-------+------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
--------------
select count(add_timestamp) from macrodb.test1 where add_timestamp >'2014-01-01 00:00:00'
--------------
+----------------------+
| count(add_timestamp) |
+----------------------+
| 3287470 |
+----------------------+
1 row in set (0.78 sec)
3.2.测试test2
3.2.1.测试COUNT(1)
--------------
explain extended select count(1) from macrodb.test2
--------------
+----+-------------+-------+-------+---------------+--------------------+---------+------+---------+----------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+-------+---------------+--------------------+---------+------+---------+----------+-------------+
| 1 | SIMPLE | test2 | index | NULL | idx_tst2_adatetime | 6 | NULL | 3274822 | 100.00 | Using index |
+----+-------------+-------+-------+---------------+--------------------+---------+------+---------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
--------------
show warnings
--------------
+-------+------+---------------------------------------------------------------------+
| Level | Code | Message |
+-------+------+---------------------------------------------------------------------+
| Note | 1003 | /* select#1 */ select count(1) AS `count(1)` from `macrodb`.`test2` |
+-------+------+---------------------------------------------------------------------+
1 row in set (0.00 sec)
--------------
select count(1) from macrodb.test2
--------------
+----------+
| count(1) |
+----------+
| 3287480 |
+----------+
1 row in set (0.81 sec)
3.2.2.测试COUNT(id)
--------------
explain extended select count(id) from macrodb.test2
--------------
+----+-------------+-------+-------+---------------+-------------+---------+------+---------+----------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+-------+---------------+-------------+---------+------+---------+----------+-------------+
| 1 | SIMPLE | test2 | index | NULL | idx_tst2_id | 9 | NULL | 3274822 | 100.00 | Using index |
+----+-------------+-------+-------+---------------+-------------+---------+------+---------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
--------------
show warnings
--------------
+-------+------+-------------------------------------------------------------------------------------------+
| Level | Code | Message |
+-------+------+-------------------------------------------------------------------------------------------+
| Note | 1003 | /* select#1 */ select count(`macrodb`.`test2`.`id`) AS `count(id)` from `macrodb`.`test2` |
+-------+------+-------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
--------------
select count(id) from macrodb.test2
--------------
+-----------+
| count(id) |
+-----------+
| 3287480 |
+-----------+
1 row in set (2.41 sec)
3.2.3.测试COUNT(*)
--------------
explain extended select count(*) from macrodb.test2
--------------
+----+-------------+-------+-------+---------------+--------------------+---------+------+---------+----------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+-------+---------------+--------------------+---------+------+---------+----------+-------------+
| 1 | SIMPLE | test2 | index | NULL | idx_tst2_adatetime | 6 | NULL | 3274822 | 100.00 | Using index |
+----+-------------+-------+-------+---------------+--------------------+---------+------+---------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
--------------
show warnings
--------------
+-------+------+---------------------------------------------------------------------+
| Level | Code | Message |
+-------+------+---------------------------------------------------------------------+
| Note | 1003 | /* select#1 */ select count(0) AS `count(*)` from `macrodb`.`test2` |
+-------+------+---------------------------------------------------------------------+
1 row in set (0.00 sec)
--------------
select count(*) from macrodb.test2
--------------
+----------+
| count(*) |
+----------+
| 3287480 |
+----------+
1 row in set (0.82 sec)
3.3.测试test3
3.3.1.测试COUNT(1)
--------------
explain extended select count(1) from macrodb.test3
--------------
+----+-------------+-------+------+---------------+------+---------+------+---------+----------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------+---------------+------+---------+------+---------+----------+-------+
| 1 | SIMPLE | test3 | ALL | NULL | NULL | NULL | NULL | 3285169 | 100.00 | NULL |
+----+-------------+-------+------+---------------+------+---------+------+---------+----------+-------+
1 row in set, 1 warning (0.00 sec)
--------------
show warnings
--------------
+-------+------+---------------------------------------------------------------------+
| Level | Code | Message |
+-------+------+---------------------------------------------------------------------+
| Note | 1003 | /* select#1 */ select count(1) AS `count(1)` from `macrodb`.`test3` |
+-------+------+---------------------------------------------------------------------+
1 row in set (0.00 sec)
--------------
select count(1) from macrodb.test3
--------------
+----------+
| count(1) |
+----------+
| 3287480 |
+----------+
1 row in set (1.43 sec)
3.3.2.测试COUNT(id)
--------------
explain extended select count(id) from macrodb.test3
--------------
+----+-------------+-------+------+---------------+------+---------+------+---------+----------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------+---------------+------+---------+------+---------+----------+-------+
| 1 | SIMPLE | test3 | ALL | NULL | NULL | NULL | NULL | 3285169 | 100.00 | NULL |
+----+-------------+-------+------+---------------+------+---------+------+---------+----------+-------+
1 row in set, 1 warning (0.00 sec)
--------------
show warnings
--------------
+-------+------+-------------------------------------------------------------------------------------------+
| Level | Code | Message |
+-------+------+-------------------------------------------------------------------------------------------+
| Note | 1003 | /* select#1 */ select count(`macrodb`.`test3`.`id`) AS `count(id)` from `macrodb`.`test3` |
+-------+------+-------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
--------------
select count(id) from macrodb.test3
--------------
+-----------+
| count(id) |
+-----------+
| 3287480 |
+-----------+
1 row in set (0.92 sec)
3.3.3.测试COUNT(*)
--------------
explain extended select count(*) from macrodb.test3
--------------
+----+-------------+-------+------+---------------+------+---------+------+---------+----------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------+---------------+------+---------+------+---------+----------+-------+
| 1 | SIMPLE | test3 | ALL | NULL | NULL | NULL | NULL | 3285169 | 100.00 | NULL |
+----+-------------+-------+------+---------------+------+---------+------+---------+----------+-------+
1 row in set, 1 warning (0.00 sec)
--------------
show warnings
--------------
+-------+------+---------------------------------------------------------------------+
| Level | Code | Message |
+-------+------+---------------------------------------------------------------------+
| Note | 1003 | /* select#1 */ select count(0) AS `count(*)` from `macrodb`.`test3` |
+-------+------+---------------------------------------------------------------------+
1 row in set (0.00 sec)
--------------
select count(*) from macrodb.test3
--------------
+----------+
| count(*) |
+----------+
| 3287480 |
+----------+
1 row in set (0.85 sec)
结论:
1.除非要统计某列非空值的总数,否则任何情况一律用COUNT(*),效率比COUNT(列名)高很多
2.除非有特殊需要,否则COUNT(*)不要加WHERE条件,会严重影响效率,如果加了条件COUNT(*)和COUNT(主键)效率是一致的,COUNT(非主键)效率很低
3.在没有WHERE条件的情况下:COUNT(*)等于COUNT(主键)优于COUNT(非主键有索引)优于COUNT(非主键无索引)
4.只要加了WHERE就会降低效率,即使是WHERE 1=1
5.COUNT(*)MySQL会转为COUNT(1),COUNT(id)会计算NOT NULL值。
6.COUNT(1)和COUNT(0)一样。都是返回包括NULL在内的值。