mysql中使用count()统计总结与思考
如果你的需要是统计总行数时,为什么要使用count(*),而避免使用指定具体的列名?
count()函数里面的参数是列名的的时候,那么会计算这个字段有值项的次数。也就是,该字段没有值的项并不会进入计算范围(就是网上常说的值为null的项不纳入统计)
很多地方都有类似表述:
COUNT ( column ) counts all non-NULL occurences (or should … 计算所有column字段为"非null"值的总数。
COUNT ( * ) counts all rows.
这样的话,你想统计的行数并不准确。更重要的是,还会增加消耗。因为,需要判断扫描所有行才知道值是否有值。
一般是建议以count(字段名)替代count(*)。实际使用中,我的理解是,你要统计那个列的总数,比如我想统计会员的总数,那么我可以以uid作为参考,uid是null的都不去扫描了
于是查询就是select count(uid) from members
如果使用count(*),它会计算总行数。不管你字段是否有值都会列入计算范围。
另外一点:myisam引擎很容易获得总行数的统计。查询速度变得更快。因为myisam存储引擎已经存储了表的总行数。
应该是每次新增加一行,这个计数器就加1。英文资料是这样子说的:
On MyISAM, doing a query that does SELECT COUNT(*) FROM {some_table}, is very fast, since MyISAM keeps the information in the index
也就是说,把表的总数缓存在索引中了。
注意一点:myisam存储引擎的表,count(*)速度快的也仅仅是不带where条件的count。这个想想容易理解的,因为你带了where限制条件,原来所以中缓存的表总数能够直接返回用吗?不能用。这个查询引擎也是需要根据where条件去表中扫描数据,进行统计返回的。
归纳:实际编程中统计总行数是经常用到的。此时使用count(*)多处可见。我很少看到有人使用列名作为参数:count(a)的情况。即使是这样使用,可能其初衷也是想统计行数。只是不知道这样所造成的细微差异而错误使用了"列名"的形式。
针对Innodb表,尽量不执行 SELECT COUNT(*)
语句,因为Innodb表没有类似MyISAM那样的内部计数器来记录表记录总量,执行这个操作将会全表扫描,速度很慢。所以呢,表的行数越多,扫描的时间就越多。当你表行数还是小数量的时候体会不出速度差距。比如百万也感觉不出明显。上千万就会很明显速度差别了。
对策:对innob存储引擎的大表进行select count()统计总数操作,业界都会尽量避免。
如果要查询innodb存储引擎的表总数,要怎么办?
总结两种方案:
方案一:通过查询information_schema库,它记录了innodb类型每个表大致的数据行数
方案二(更优):涉及到总数操作,专门维护一个总数。新注册一个会员,总数值加1,需要总数的时候直接拿这个总数,比如分页时。
方案二的扩展性更好,随着会员表数量增大,水平切分会员表,要获取用户总数。可以专门看这张表。
在网上看到有人问自己的innodb存储引擎的表数据行数到10亿了,要进行分页,count(*)操作很慢怎么解决。
其实像对大数据量进行统计总数的操作,并不是要求非常精准的总数,没好多人关心差几个。像网易,凤凰网这些新闻有很多人评论,这个评论总数,如果每次点击分页的时候都进行一次count操作,那速度肯定不会快到哪里去。他们一般也是采用计数器的办法。每次新增加一条评论,就把值加1。这样分页的时候直接拿这个总数进行分页。
总数可能是存在内存中,这样分页计算的时候速度很快。累加操作的时候将内存中的值加1。总数这个值要持久化,还是要存到磁盘上的,也就是数据库中(可以是关系型数据库,也可以是mongdb这样的数据库很适合存储计数)。把总数放在内存中,只是避免频繁的磁盘i/0操作(操作数据库就要涉及到磁盘读写)。
ps:这种计数器的思想在计算机的很多地方都会用到。比如mysql数据库他自己的information_schema库维护每个表的row总数,就是这样的方式。
我有个疑问:针对innodb存储引擎,使用count(1)替代count(*)就避免全表扫描,速度很快吗?
orcalce数据库有专门对count(1)和count(*)的区别。
count(1)其实这个1,并不是表示第一个字段,而是表示一个固定值。
count(1),其实就是计算一共有多少符合条件的行。
1并不是表示第一个字段,而是表示一个固定值。
其实就可以想成表中有这么一个字段,这个字段就是固定值1,count(1),就是计算一共有多少个1.
同理,count(2),也可以,得到的值完全一样,count('x'),count('y')都是可以的。一样的理解方式。在你这个语句理都可以使用,返回的值完全是一样的。就是计数。
count(*),执行时会把星号翻译成字段的具体名字,效果也是一样的,不过多了一个翻译的动作,比固定值的方式效率稍微低一些。
count(1)就是不指定具体字段,固定值。
网上找不到源码分析出count(1)到底做了什么。如果参考oracle数据库的解释,其实也不见得就能避免全表扫描