count()聚合函数正确用法

count()聚合计算

  count()是聚合函数,对于返回的结果集,一行行地判断,累计值加1,最后返回累计值,count(*)、count(主键ID)和count(1)表示返回知足条件的结果集的总行数。mysql

count()聚合函数统计非NULL与NULL值的区别:sql

  一、count(字段)不统计NULL记录,即表示知足条件的数据行里参数字段不为NULL的行函数

  二、count(1)和count(*)会记录NULL值性能

count(主键ID)、count(字段)、count(1)、count(*)的区别和性能差别(分析性能差异的原则)
  一、server层要什么就给什么 
  二、InnoDB只给必要的值
  三、如今的优化器对count(*)的取行数作了优化,其余没有作优化
 
count(主键ID)比count(1)慢的缘由
  对于 count(主键 ID) 来讲,InnoDB 引擎会遍历主键索引树,把每一行的ID值取出来,返回给server层,server层拿到ID后,判断是不可能为空的,按行累加加1,最后返回累计值。
  对于count(1),InnoDB引擎会扫描主键索引树,但不取值,server层对于返回的每一行,按行累计加1,判断不可能为NULL,返回累计值。
  从InnoDB引擎层返回ID会涉及到解析数据行、拷贝字段值的操做,所以count(主键 ID)执行要比count(1)执行慢。
 
count(字段)
  一、若是这个字段定义为not null的话,一行行地从记录里面读出这个字段,判断不能为null,按行累计加1
  二、若是这个字段定义容许为null,一行行地从记录里面读出这个字段,执行的时候还要判断是否为null,不为null的按行累计加1,返回累加值
  
count(主键id)走主键索引的时候效率较count(*)差的缘由?
  平时咱们检索一列的时候,基本上等值或范围查询,那么索引基数大的索引必然效率很高(符合走主键索引查找速度最快的原则)。
  可是在作count(*)的时候并无检索具体的一行或者一个范围,那么选择基数小的索引对count操做效率会更高。在作count操做的时候,mysql会遍历每一个叶子节点,因此基数越小,效率越高。mysql非聚簇索引叶子节点保存指向主键ID的指针,因此须要检索两遍索引。可是这里相对于遍历主键索引,即便检索两遍索引效率也比单纯的检索主键索引快。
  Innodb是索引组织表,主键索引树的叶子节点是数据,而普通索引树的叶子节点是主键值,索引普通索引树小不少,索引长度越小树的大小就越小。
 
MyISAM与InnoDB,正如在不一样的存储引擎中,count(*)函数的执行是不一样的
  在MyISAM存储引擎中,count()函数是直接读取数据表保存的行记录数并返回,效率很高,可是若是添加了where条件的话,MyISAM表也不能返回得很快。
  在InnoDB存储引擎中,count(*)函数是先从内存中读取表中的数据到内存缓冲区,而后扫描全表得到行记录数。在使用count函数中加上where条件时,在两个存储引擎中的效果是同样的,都会扫描全表计算某字段有值项的次数。
count(*)中关于select count(*) from tab_name几种不走索引和走那种索引情景分析
CREATE TABLE `t1` (
  `c1` varchar(30) NOT NULL,
  `c2` varchar(20) NOT NULL,
  `c3` varchar(40) NOT NULL,
  `c4` varchar(10) DEFAULT NULL)
ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='ceshi_count'
一、表中没有任何索引(表也没有主键)
mysql> explain select count(*) from t1;
+----+-------------+-------+------+---------------+------+---------+------+------+-------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+------+-------+
|  1 | SIMPLE      | t1    | ALL  | NULL          | NULL | NULL    | NULL |    1 | NULL  |
+----+-------------+-------+------+---------------+------+---------+------+------+-------+
1 row in set (0.01 sec)
二、表有主键则执行主键索引全扫描
mysql> alter table t1 add primary key (c1);
Query OK, 0 rows affected (0.16 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> explain select count(*) from t1;
+----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
| id | select_type | table | type  | possible_keys | key    | key_len | ref  | rows | Extra      |
+----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
|  1 | SIMPLE      | t1    | index | NULL          | PRIMARY | 92      | NULL |    1 | Using index |
+----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
1 row in set (0.00 sec)
三、表有二级索引,则使用二级索引key_len最小的索引进行扫描,尽管这个二级索引的key_len的值大于主键,都使用二级索引
mysql> alter table t1 add index idx_c3(c3);
Query OK, 0 rows affected (0.04 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> explain select count(*) from t1;
+----+-------------+-------+-------+---------------+--------+---------+------+------+-------------+
| id | select_type | table | type  | possible_keys | key    | key_len | ref  | rows | Extra       |
+----+-------------+-------+-------+---------------+--------+---------+------+------+-------------+
|  1 | SIMPLE      | t1    | index | NULL          | idx_c3 | 122     | NULL |    1 | Using index |
+----+-------------+-------+-------+---------------+--------+---------+------+------+-------------+
1 row in set (0.00 sec)

四、表有多个二级索引,则使用key_len小的二级索引进行扫描优化

mysql> alter table t1 add index idx_t1_c4(c4);
Query OK, 0 rows affected (0.03 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> explain select count(*) from t1;
+----+-------------+-------+-------+---------------+-----------+---------+------+------+-------------+
| id | select_type | table | type  | possible_keys | key      | key_len | ref  | rows | Extra      |
+----+-------------+-------+-------+---------------+-----------+---------+------+------+-------------+
|  1 | SIMPLE      | t1    | index | NULL          | idx_t1_c4 | 33      | NULL |    1 | Using index |
+----+-------------+-------+-------+---------------+-----------+---------+------+------+-------------+
1 row in set (0.00 sec)
取表行数的几种方式
  一、count(*)取行数
  二、经过infomation_schema能够快速拿到表的count值,但不是一个准确的值,经过show table status like 'tab_name'查找到的table rows是经过采样方式获得行数,它的偏差率达到了40到50%,
  三、MyISAM会存储具体的行数(可能由于myISAM事务要加表锁,才这样设计),InnoDB则须要进行全表扫描