目录
1.背景
出去找工作,或者在开发的时候,或者初学的时候,可能会遇到count(1)和count(*)有什么区别呢?那个效率更高呢?
2.常见的面试回答
在 MySQL 数据库中,COUNT()
函数是一个聚合函数,用于计算表中的行数或者满足特定条件的行数。COUNT()
函数可以有不同的参数,主要包括 COUNT(1)
、COUNT(*)
和 COUNT(字段)
,它们之间在功能和使用上有所区别,但主要区别在于性能和语义上的一些细微差别。
-
COUNT(*)
COUNT(*)
会计算包括 NULL 值在内的所有行数。这是因为它不依赖于任何列的值,而是直接计算表中的行数。- 在大多数情况下,
COUNT(*)
是性能最优的选择,因为数据库优化器能够利用索引或者其他统计信息来快速计算出表中的行数,特别是在不需要过滤数据(即没有 WHERE 子句)时。 COUNT(*)
的语义非常清晰,表示"计算所有行"。
-
COUNT(1)
COUNT(1)
在逻辑上等同于COUNT(*)
,因为它并不真正依赖于表达式1
的值。数据库引擎在处理COUNT(1)
时,也是计算所有行数,包括包含 NULL 值的行。- 尽管
COUNT(1)
和COUNT(*)
在逻辑上等价,但在某些数据库系统(包括 MySQL)中,COUNT(*)
可能因为优化器的特殊优化而具有更好的性能。 - 使用
COUNT(1)
的主要原因可能是出于习惯或者是在其他数据库系统中,COUNT(1)
可能具有特定的优化。但在 MySQL 中,推荐使用COUNT(*)
。
-
COUNT(字段)
COUNT(字段)
会计算指定字段中非 NULL 值的数量。如果字段中包含 NULL 值,那么这些 NULL 值不会被计入总数。- 当你需要计算某个特定字段的非空值数量时,应该使用
COUNT(字段)
。 - 相比于
COUNT(*)
和COUNT(1)
,COUNT(字段)
的性能可能会稍差,特别是当该字段未被索引且表中包含大量数据时,因为数据库需要扫描整个表来统计非 NULL 值。
总结:
- 在大多数情况下,如果你只是需要计算表中的行数,推荐使用
COUNT(*)
,因为它具有最好的性能和最清晰的语义。 COUNT(1)
和COUNT(*)
在 MySQL 中性能上几乎没有区别,但推荐使用COUNT(*)
。- 当你需要计算某个字段中非 NULL 值的数量时,使用
COUNT(字段)
。
3.有经验的面试回答
面试回答思路: 面试本质:不是点对点回答问题,而是面试官通过提出一个话题(问题),获取到面试者的思维和技术水平; 1.回答基本定义 2.说说实际生产中的运用 3.引导到自己擅长的技术点上深入探讨
count函数的语义
count() 是一个聚合函数,函数的参数不仅可以是字段名,也可以是其他任意表达式,该函数作用是统计符合查询条件的记录中,函数指定的参数不为 NULL 的记录有多少个。
在通过 count 函数统计有多少个记录时,MySQL 的 server 层会维护一个名叫 count 的变量。
server 层会循环向 InnoDB 读取一条记录,如果 count 函数指定的参数不为 NULL,那么就会将变量 count 加 1,直到符合查询的全部记录被读完,就退出循环。最后将 count 变量的值发送给客户端。
实际生产中的应用
在实际生产中我们最常见的应用场景是做分页的时候会用到count(*),
当然也不是自己写sql写的count(*),一般是分页插件自动生成的sql语句查询总条数,如果观察一下日志输出就会发现使用的是count(*),
因此个人觉得count(*)应该是COUNT(*)和COUNT(1)和COUNT(字段)中效率想对来说较好的;
技术探讨
另外结合mysql的数据库的特性(逻辑架构和sql的执行流程)
count(*)和count(1) 估计效率应该差不多吧!
因为sql在执行的时候会经过这几个流程(链接->查询缓存->解析sql->优化sql->执行sql->返回结果),
这样来思考的话,那么count(*)和count(1)很有可能会被优化器优化成一样;
再来说一下count(字段),说到字段,就要分析3中情况
1.不是索引的情况
2.count(二级索引)是索引,但不是主键的情况
3.count(id)是主键
不是索引的话效率一定会很低,需要扫描整个数据库表,效率低于count(*)和count(1)
count(id)通常是主键索引,在 InnoDB 中,主键索引是聚簇索引,它存储了实际的数据行。执行 count 时,InnoDB 需要遍历整个聚簇索引来统计行数。
count(二级索引)是指存储了索引列和主键列的指针,而不包含实际的数据行。因此,二级索引相对来说更小。执行 count 时,InnoDB 只需要遍历这个较小的二级索引,而不是整个聚簇索引,需要读取的数据页更少,所以成本更低。
当然,理论归理论,具体情况具体分析,具体的性能差异取决于索引的大小和表的结构,可以用 explain 语句查看查询计划和成本。
实际生产用法
最后, 如果表的数据量过大,比如千万级别,一般来说都不会在频繁使用count来统计总条数,无论用那种count的方式都会很慢
常见的做法是
1.比如在分页的时候,不会显示总条数,查询数据需要传入最小id,这也是有弊端的比如不能从第二页转到第五页等;比如之前我们在做拉取某东的产品的时候,就会发现拉取产品需要传入上次处理的最后的产品id;
2.弄一个中间表存储表的总行数等;
总之,当数据量大的时候不仅仅是需要技术侧努力优化,还需要结合业务,看看如何做更合理,更高效,性价比更高!
4.总结&评论
从上面的2种回答中,
你会发现第一种是典型的点对点的回答方式,也是大多数面试者回答的方式,就像我们读书的时候做考试卷子一样的回答;
第二种回答,更偏向口头沟通,更在乎思维,层层递进,每得出一个结论都给出了是如何思考的,并且给出了实际生产中的做法和将会遇到的问题;
如果你面试官,你会觉得那个更容易面上呢?
在评论区给出你个观点吧!