一篇彻底吃透MySQL中count(*)、count(1)、count(字段)的区别(不踩坑)

 更新时间:2026年04月23日 08:13:42   作者:what丶k  
COUNT顾名思义就是计数的意思,此函数用处就是对表中记录数进行计数,这篇文章主要介绍了MySQL中count(*)、count(1)、count(字段)区别的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下

前言

在 MySQL 日常开发中,统计数据行数是最常见的操作之一,而 count(*)、count(1)、count(字段)这三种写法,几乎是每个开发者都会用到的。但很多人只知道它们都能“统计数量”,却不清楚三者在底层执行逻辑、统计范围、性能表现上的差异,甚至在面试中被问到相关问题时只能含糊其辞。

今天这篇博客,就从 MySQL 底层原理出发,结合实际场景案例,彻底讲清楚这三种 count 写法的区别,帮你在开发中精准选型、避免踩坑,同时也能轻松应对面试中的相关问题。

一、先明确核心前提:count 函数的本质

在拆解区别之前,我们首先要明确一个核心点:MySQL 中的 count()函数,本质是“统计符合条件的、非 NULL 值的数量”——这是理解三者区别的基础,也是最容易被忽略的关键点。

这里需要提前区分两个概念:“行存在”和“字段非 NULL”。行存在不等于字段非 NULL,一个行可能所有字段都是 NULL(只要表结构允许),但它依然是一条有效的行,会被某些 count 写法统计到。

二、逐个拆解:count(*)、count(1)、count(字段)的底层逻辑

我们分别从“定义、执行原理、特点”三个维度,逐个解析三种写法,结合 InnoDB 引擎(目前 MySQL 主流引擎)的特性讲解(MyISAM 引擎因使用场景极少,暂不重点讨论)。

1. count(*)

定义​:统计表中所有有效行的数量(无论行中的字段是否为 NULL,只要行存在,就会被统计)。

执行原理​:InnoDB 引擎对 count(*)做了特殊优化——它不会去扫描表中的具体字段,而是直接扫描表的“聚簇索引”(主键索引),统计聚簇索引的行数。因为聚簇索引是 InnoDB 表的核心索引,每个行都必然存在聚簇索引条目,所以这种扫描效率极高。

这里有一个常见误区:认为 count(*)会扫描表中所有字段,性能最差。实际上恰恰相反,count(*)是 MySQL 优化器最认可的统计方式,优化器会自动选择最高效的路径(聚簇索引扫描),无需额外判断字段是否为 NULL。

特点​:统计所有行(包括字段全为 NULL 的行);性能最优(InnoDB 下);无需关注任何字段,通用性最强。

2. count(1)

定义​:以“1”作为占位符,统计表中所有有效行的数量(与 count(*)类似,无论字段是否为 NULL,只要行存在就会被统计)。

执行原理​:count(1)的执行逻辑与 count(*)非常接近,但略有区别——它不会扫描表中的具体字段,而是为每一行分配一个“占位符 1”,然后统计“1”的数量(因为每一行都能分配到 1,所以本质还是统计行的数量)。

在 InnoDB 引擎中,优化器会将 count(1)优化成与 count(*)几乎一致的执行计划,也就是说,两者的性能差异微乎其微,几乎可以忽略不计。只有在表中没有主键索引、且存在大量数据时,才可能出现极细微的性能差距(差距在毫秒级,日常开发可忽略)。

特点​:统计所有行(包括字段全为 NULL 的行);性能与 count(*)基本一致;不依赖任何字段,写法上是 count(*)的一种替代方案。

3. count(字段)

定义​:统计表中“该字段值非 NULL”的行的数量(注意:仅统计字段不为 NULL 的行,字段为 NULL 的行会被排除)。

执行原理​:count(字段)的执行逻辑与前两者有本质区别——它需要扫描指定的字段,逐行判断该字段的值是否为 NULL,只有非 NULL 的值才会被统计。其性能表现,完全取决于该字段是否有索引:

  • 如果字段是​主键字段​(聚簇索引):InnoDB 会直接扫描聚簇索引,判断主键字段是否为 NULL(主键字段默认非 NULL,所以本质还是统计所有行),性能接近 count(*)和 count(1)。
  • 如果字段是​非主键索引字段​(二级索引):InnoDB 会扫描该二级索引,判断字段是否为 NULL,性能略低于 count(*)(因为二级索引的条目比聚簇索引小,但需要额外判断 NULL)。
  • 如果字段​没有索引​:InnoDB 会进行“全表扫描”,逐行读取该字段的值并判断是否为 NULL,性能最差(尤其是数据量较大时,会明显拖慢查询速度)。

这里有一个关键提醒:count(字段)统计的是“非 NULL 值的数量”,而不是“字段有值的数量”——如果字段的值是空字符串(‘’)、0 等非 NULL 值,依然会被统计;只有字段值为 NULL 时,才会被排除。

特点​:仅统计指定字段非 NULL 的行;性能取决于字段是否有索引;灵活性强(可针对性统计某字段的有效数据),但易踩坑。

三、核心区别汇总:一张表看懂三者差异

为了更直观地对比,我们用表格汇总三者的核心区别(基于 InnoDB 引擎,默认表有主键索引):

统计方式统计范围NULL 值处理执行原理性能表现
count(*)所有有效行(行存在即统计)不忽略任何 NULL(包括字段全 NULL 的行)扫描聚簇索引,统计行数(优化器最优选择)最优(推荐)
count(1)所有有效行(行存在即统计)不忽略任何 NULL(包括字段全 NULL 的行)占位符统计,优化器优化后接近 count(*)与 count(*)基本一致(可替代)
count(字段)该字段非 NULL 的行忽略该字段为 NULL 的行扫描指定字段(有索引扫索引,无索引全表扫),判断非 NULL主键字段 ≈count(*);非主键索引略差;无索引最差

四、实际开发选型建议:避免踩坑,高效统计

结合上面的分析,给出日常开发中最实用的选型建议,帮你避开误区,提升查询效率:

1. 统计“表中总记录数”——优先用 count(*)

无论是从性能还是通用性来看,count(*)都是最优选择。InnoDB 优化器会自动为其选择最高效的执行路径,无需担心性能问题。

误区规避:不要因为担心“扫描所有字段”而改用 count(1),两者性能几乎无差异,而 count(*)是 MySQL 官方推荐的写法,可读性更强。

2. 统计“某字段的有效数据量”(非 NULL)——用 count(字段)

如果需要统计某个字段不为 NULL 的数据量(比如统计有邮箱的用户数、有手机号的订单数),就用 count(字段)。

性能优化:如果该字段需要频繁用于 count 统计,建议给该字段建立索引(二级索引即可),避免全表扫描,提升性能。

误区规避:不要用 count(字段)统计总记录数,尤其是非索引字段,会导致全表扫描,性能极差;同时注意区分“NULL”和“空字符串”,避免统计结果出错。

3. count(1)的使用场景——作为 count(*)的替代方案

count(1)的性能与 count(*)基本一致,唯一的区别在于写法习惯。有些开发者习惯用 count(1),尤其是在表中字段较多、担心 count(*)扫描字段的场景下(虽然这种担心是多余的)。

建议:如果团队有统一写法,遵循团队规范即可;如果没有,优先用 count(*),可读性更强,更符合 MySQL 官方推荐。

4. 特殊场景:count(DISTINCT 字段)

如果需要统计某个字段“非 NULL 且不重复”的数量(比如统计不同省份的用户数),可以用 count(DISTINCT 字段)。但注意:count(DISTINCT 字段)会扫描字段并去重,性能比普通 count 更低,大数据量下需谨慎使用(可考虑提前缓存结果)。

五、常见面试题延伸:为什么 count(*)比 count(字段)快?

这是面试中高频问到的问题,结合前面的原理,总结核心答案(简洁好记):

  1. count(*):InnoDB 优化器会扫描聚簇索引,直接统计行数,无需判断任何字段的 NULL 值,效率最高;
  2. count(字段):需要扫描指定字段,逐行判断字段是否为 NULL,若字段无索引,还会进行全表扫描,额外增加了判断和扫描成本,所以比 count(*)慢。

补充:如果 count(字段)中的字段是主键,那么性能接近 count(*),因为主键字段默认非 NULL,无需判断 NULL 值,扫描聚簇索引即可。

六、总结

其实 count(*)、count(1)、count(字段)的核心区别,本质是“统计范围”和“执行原理”的差异:

  • count(*) 和 count(1) 是“统计行的数量”,不关心字段是否为 NULL,性能最优;
  • count(字段) 是“统计字段非 NULL 的数量”,性能取决于字段是否有索引,灵活性强但易踩坑。

日常开发中,记住“统计总数用 count(*),统计字段有效数用 count(字段)”,就能避开大部分误区,同时保证查询效率。

最后,希望这篇博客能帮你彻底搞懂三者的区别,无论是日常开发还是面试,都能从容应对。

到此这篇关于MySQL中count(*)、count(1)、count(字段)区别的文章就介绍到这了,更多相关MySQL中count(*)、count(1)、count(字段)内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • MySQL实现数据批量更新功能详解

    MySQL实现数据批量更新功能详解

    最近需要批量更新大量数据,习惯了写sql,所以还是用sql来实现,下面这篇文章主要给大家总结介绍了关于MySQL批量更新的方式,需要的朋友可以参考下
    2023-02-02
  • MySQL实现批量插入测试数据的方式小结

    MySQL实现批量插入测试数据的方式小结

    在开发过程中我们不管是用来测试性能还是在生产环境中页面展示好看一点, 经常需要一些测试数据, 本文主要介绍了两种常用的MySQL测试数据批量生成方式,希望对大家有所帮助
    2023-11-11
  • mysql 5.7.13 安装配置方法图文教程(linux)

    mysql 5.7.13 安装配置方法图文教程(linux)

    这篇文章主要为大家详细介绍了linux下mysql 5.7.13 安装配置方法图文教程,感兴趣的小伙伴们可以参考一下
    2016-06-06
  • Navicat中如何导入数据库SQL脚本并执行

    Navicat中如何导入数据库SQL脚本并执行

    这篇文章主要给大家介绍了关于Navicat中如何导入数据库SQL脚本并执行的相关资料,Navicat是一个强大的MySQL数据库管理和开发工具,文中通过图文介绍的非常详细,需要的朋友可以参考下
    2023-07-07
  • MySQL分区表的详细介绍

    MySQL分区表的详细介绍

    当设计MySQL分区表时,需要考虑以下几个方面,分区策略、分区字段、分区数量和分区函数,本文就来介绍一下MySQL分区表,感兴趣的可以了解一下
    2023-10-10
  • MySQL查询优化:连接查询排序浅谈

    MySQL查询优化:连接查询排序浅谈

    不知道有没有人碰到过这样恶心的问题:两张表连接查询并limit,SQL效率很高,但是加上order by以后,语句的执行时间变的巨长,效率巨低。下边就来看看这个问题需要如何解决
    2013-02-02
  • mysql5.7.19 安装配置方法图文教程(win10)

    mysql5.7.19 安装配置方法图文教程(win10)

    这篇文章主要为大家分享了win10下mysql 5.7.19 安装配置方法图文教程,感兴趣的朋友可以参考一下
    2017-07-07
  • 利用MySQL函数实现判断视频扩展名的代码

    利用MySQL函数实现判断视频扩展名的代码

    MySQL拥有强大的自定义函数功能,如下,我写了一个用MySQL函数 判断视频地址是否可以手机端播放
    2012-02-02
  • Mysql数据库乐观锁与悲观锁示例详解

    Mysql数据库乐观锁与悲观锁示例详解

    乐观锁和悲观锁是并发控制的一种机制,用于多线程或多进程环境下对共享资源的访问管理,以防止数据不一致或竞态条件,这篇文章主要介绍了Mysql数据库乐观锁与悲观锁的相关资料,需要的朋友可以参考下
    2026-04-04
  • mysql 8.0.22压缩包完整安装与配置教程图解(亲测安装有效)

    mysql 8.0.22压缩包完整安装与配置教程图解(亲测安装有效)

    这篇文章主要介绍了mysql 8.0.22压缩包完整安装与配置教程图解(亲测安装有效),本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-12-12

最新评论