count(1)、count(具体字段)和count(*)究竟有什么区别?

本文解析了MySQL中count(*)、count(1)及count(具体字段)的区别与执行效率。通过实例展示了不同场景下这些函数如何工作,并解释了它们在InnoDB与MyISAM存储引擎中的表现差异。

在MySQL中统计数据表的行数,可以使用三种方式:select count(*) select count(1) 和 select count(具体字段),使用这三者之间的查询效率是怎样的?

前提:如果你要统计的是某个字段的非空数据行数,则另当别论,毕竟比较执行效率的前提是结果一样才可以。

假设我们有表people,有100万条数据。

CREATE TABLE `people` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `zipcode` varchar(20) COLLATE utf8_bin DEFAULT NULL,
  `firstname` varchar(20) COLLATE utf8_bin DEFAULT NULL,
  `lastname` varchar(20) COLLATE utf8_bin DEFAULT NULL,
  `address` varchar(50) COLLATE utf8_bin DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `zip_last_first` (`zipcode`,`lastname`,`firstname`)
) ENGINE=InnoDB AUTO_INCREMENT=1000001 DEFAULT CHARSET=utf8 COLLATE=utf8_bin

那么如下三条SQL统计行数结果为:

# 1000000 条数据  414ms
select count(*) from people p ; 

# 1000000 条数据
select count(1) from people p ;

# 999999条数据
select count(zipcode) from people p ;

count(*) 和 count(1)的执行计划

explain select SQL_NO_CACHE count(*) from people p ; 

在这里插入图片描述

explain  select SQL_NO_CACHE count(1) from people p ;

在这里插入图片描述

可以发现,二者执行计划是一模一样。对比二者的查询成本也能说明这一点:

在这里插入图片描述

从统计数据行结果来讲

count(*)count(1) 都是对所有结果进行count,二者本质没有区别(二者执行时间可能略有差别,不过你还是可以把它两的执行效率看成是相等的)。如果有 where 子句,则是对所有符合筛选条件的数据行进行统计。如果没有where子句,则是对数据表的数据行数进行统计。

count(具体字段) 则会过滤掉为null的数据。

如果是MyISAM存储引擎,统计数据表的行数只需要O(1)的复杂度,这是因为每张MyISAM的数据表都有一个 meta 信息存储了 row_count 值,而一致性则由表级锁来保证。

如果是InnoDB存储引擎,因为InnoDB支持事务,采用行级锁和MVCC机制,所以无法像MyISAM一样,维护一个 row_count 变量,因此需要采用 扫描全表 ,进行循环+计数的方式来完成统计。

使用什么索引进行统计?

在InnoDB引擎中,如果采用 count(具体字段) 来统计数据行数,要尽量采用二级索引。因为主键采用的索引是聚簇索引,聚簇索引包含的信息很多,显然会大于二级索引(非聚簇索引)。

对于count(*) 和 count(1) 来说,他们不需要查找具体的行,只是统计行数,系统会自动采用占用空间更小的二级索引来进行统计。

如果有多个二级索引,会使用 key_len 小的二级索引进行扫描。当没有二级索引的时候,才会采用主键索引来进行统计。

### SQL中 `COUNT(1)`、`COUNT(*)` `COUNT(字段)` 的区别 #### 一、基本概念 `COUNT()` 是 SQL 中用于统计满足条件的行数的一个聚合函数。其参数可以是一个常量(如 `1` 或 `*`)、一个具体字段名称或者表达式。 - **`COUNT(*)`**: 统计表中的所有行,无论这些行是否有 NULL 值[^4]。 - **`COUNT(1)`**: 类似于 `COUNT(*)`,因为它也统计每一行的存在性而非具体值[^2]。 - **`COUNT(字段)`**: 只统计指定字段中有非 NULL 值的行数。 --- #### 二、性能差异 尽管在理论上 `COUNT(*)` `COUNT(1)` 功能相同,但在实际执行过程中可能存在细微差别: - 当数据库优化器能够识别到 `COUNT(*)` `COUNT(1)` 并将其视为等价操作时,两者的性能几乎无异[^3]。 - 对于小型数据集(例如小于一万条记录),`COUNT(1)` 可能稍微优于 `COUNT(*)`,因为前者可能不需要解析元数据来确认哪些列为有效列。 - 如果目标是统计某特定字段的有效值,则应使用 `COUNT(字段)` 来排除那些该字段为 NULL 的情况。 --- #### 三、适用场景 以下是三种形式的具体应用场景及其优劣对比: 1. **`COUNT(*)`** - 使用范围广,适用于任何情况下需要知道整个表格有多少行的情况。 - 不依赖于某个特定字段是否存在或是否含有 NULL 值。 2. **`COUNT(1)`** - 表现类似于 `COUNT(*)`,但由于传递的是固定数值作为输入给定值,在某些实现里可能会带来轻微效率提升。 - 同样不会考虑单个字段的内容特性,仅关注每一条记录本身存在与否。 3. **`COUNT(字段)`** - 特别适合用来检测某一列内具有实际意义的数据数量——即忽略掉其中可能出现的空白项(NULL)。 - 若关心的是特定属性上的填充率或者其他基于非缺失值得出的结果,则这是最佳选项之一。 --- #### 四、案例演示 下面通过一段简单的例子展示这几种方法如何工作以及它们之间的不同之处: 假设有一个名为 `employees` 的员工信息表结构如下所示: | id | name | salary | |----|---------|--------| | 1 | Alice | 5000 | | 2 | Bob | | | 3 | Charlie | 7000 | 查询语句及对应解释分别为: ```sql -- 查询总共有几笔资料 (含NULL) SELECT COUNT(*) FROM employees; -- 结果应该是3 [^1] -- 查询同样也是总共几笔资料 (含NULL), 效果同上 SELECT COUNT(1) FROM employees; -- 结果同样是3 -- 查询薪水栏位有填入金额的人数 (不含NULL) SELECT COUNT(salary) FROM employees; -- 结果将是2, 因为第二个人没有工资资讯 [^4] ``` --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

流烟默

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值