面试常被忽略的问题——count

你好,我是吴计可师,一个工作十多年的后端开发,曾就职京东、阿里等多家互联网头部企业。

点击下方👇关注公众号,带你一起复习后端技术,看看面试考点,补充积累技术知识,每天都为面试准备积累


今天我来聊一聊mysql中,count相关的一些面试题


在 SQL 查询中,COUNT(*)、COUNT(1) 和 COUNT(id) 是常见的计数方式,它们的功能是统计记录数,但具体有什么不同?



01
COUNT(*)


作用:统计表中所有行的数量,包括所有列,不依赖具体字段。

特点

  • 它会对每一行进行统计,无论行中是否有 NULL。

  • 在数据库底层实现中,COUNT(*) 不会逐一获取列值,而是通过优化器直接统计行数,因此性能通常是最优的。

适用场景:统计表的总行数,通常是最常用的计数方式。

示例:

SELECT COUNT(*) FROM employees;

结果:返回 employees 表的总行数。


02
COUNT(1)


作用:统计表中所有行的数量,与 COUNT(*) 的效果相同。

特点

  • COUNT(1) 会把 1 作为一个常量列,类似于 COUNT(*),但实际不涉及表的具体字段。

  • 在一些数据库(如 Oracle)中,COUNT(1) 的性能与 COUNT(*) 相同,因为它们都通过优化器直接统计行数。

  • 注意:在某些情况下,数据库会对 1 做额外解析,但现代数据库优化器通常能将其优化为与 COUNT(*) 相同的效果。

适用场景:和 COUNT(*) 类似,通常用于统计总行数。

示例:

SELECT COUNT(1) FROM employees;

结果:返回 employees 表的总行数。


03
COUNT(id)


作用:统计某一列的非 NULL 值的行数。

特点

  • 只统计 id 列中值不为 NULL 的行。

  • 如果列 id 中有 NULL,这些行不会被计入。

  • 性能:需要读取 id 列的具体值,性能可能略低于 COUNT(*) 或 COUNT(1)。

适用场景:统计特定列的非空数据数量。

示例:

SELECT COUNT(id) FROM employees;

结果:返回 employees 表中 id 列非 NULL 值的行数,但是一般id如果是主键的话,也不为空


04
性能比较



现代数据库:COUNT(*) 和 COUNT(1) 的性能几乎相同,因为优化器会将它们优化为直接统计行数的操作。

COUNT(id) 的性能取决于 id 列是否包含 NULL 值,以及该列是否被索引。

  • 如果列有索引,性能可能会接近 COUNT(*)。

  • 如果列中有大量 NULL 或未被索引,可能会稍慢。



05
总结


推荐根据实际需求选择:

  • 统计总行数:优先使用 COUNT(*) 或 COUNT(1)。

  • 统计某列非空行数:使用 COUNT(id)。


06
其他问题


6.1 在 MyBatis-Plus 中,默认生成的是哪种形式?

当使用 count() 方法进行查询时,默认会转换成 COUNT(*) 的 SQL 语句。

MyBatis-Plus 是基于 MyBatis 的增强框架,其核心是自动生成高效的 SQL,而 COUNT(*) 是数据库中最优化的方式之一,因此 MyBatis-Plus 默认采用这种写法。


示例代码

假设有一个 User 表,结构如下:

idnameage
1Alice25
2BobNULL

使用 MyBatis-Plus 的 count() 查询:

// 使用 MyBatis-Plus 提供的 BaseMapper 接口int totalUsers = userMapper.selectCount(null);System.out.println("用户总数:" + totalUsers);

生成的 SQL:

SELECT COUNT(*) FROM user;


当然也可以自定义:
QueryWrapper<User> queryWrapper = new QueryWrapper<>();queryWrapper.isNotNull("age"); // 只统计 age 不为 NULL 的行int count = userMapper.selectCount(queryWrapper);

生成的 SQL:

SELECT COUNT(*) FROM user WHERE age IS NOT NULL;


6.2 如果我的count(*)执行需要10s,而期间插入的10条数据是否会被计入?

在大多数数据库系统(如 MySQL、PostgreSQL)中,COUNT(*) 的执行是基于当前事务的一致性快照,而不会实时更新数据。所以在执行 COUNT(*) 过程中插入的 10 条数据通常不会被计入

具体来说:

  • 默认情况下:如果 COUNT(*) 在一个事务中运行,数据库会使用该事务开始时的快照数据,这样就不会包括在 COUNT(*) 开始执行后插入的新数据。

  • 隔离级别影响:事务隔离级别决定了并发查询时数据的可见性。在隔离级别为“可重复读”或更高的情况下,COUNT(*) 将基于查询开始时的快照,确保一致的结果,不会包括新插入的数据。

总结而言,大多数情况下,在 COUNT(*) 执行时插入的数据不会计入。


今天就分享到这儿,喜欢可以点赞,关注。你的关注是我前进的动力!

END


扫码关注

一起积累后端知识
不积跬步,无以至千里
不积小流,无以成江海



喜欢此内容的人还喜欢

谈谈id那些事(五)——美团的 Leaf 的ID生成


一个阿里二面面试官必问的问题


谈谈id那些事(三)——阿里巴巴的 TDDL的ID生成


面试常被忽略的问题——内存区域划分


Java类加载过程详解