count(1),count(*),count(主键) 性能对比及辟谣

本文对比了MySQL和SQL Server中count(*)、count(1)及count(主键)的性能,发现不同数据库优化策略各异。MySQL中三者性能相当,而SQL Server中count(主键)更优。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

前段时间关于统计数量的sql问题和朋友进行了讨论,网上关于这三种查询方式说法不一,主要有以下两种说法。

  1. count(*) = count(主键) > count(1)
  2. count(主键) > count(*) > count(1)

今天对这三种方式进行探究。

数据库为mysql 5.7.12,引擎为InnoDB。

建表

CREATE TABLE `user` (
  `id` int(32) NOT NULL AUTO_INCREMENT,
  `name` varchar(500) DEFAULT NULL COMMENT '姓名',
  `deleted` int(2) NOT NULL DEFAULT '1' COMMENT '逻辑删除',
  `created_date` datetime DEFAULT NULL COMMENT '创建时间',
  `created_by` varchar(255) DEFAULT NULL,
  `update_date` datetime DEFAULT NULL,
  `update_by` varchar(255) DEFAULT NULL,
  `version` int(11) NOT NULL DEFAULT '1' COMMENT '乐观锁',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1502726 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='用户表';

循环插入150万条数据。

DROP PROCEDURE
IF
	EXISTS proc_initData;
DELIMITER $
CREATE PROCEDURE proc_initData () BEGIN
	DECLARE
		i INT DEFAULT 1;
	WHILE
			i <= 5000000 DO
			INSERT INTO user ( name, created_date, update_date )
		VALUES
			( '哈哈哈啊哈哈哈', NOW(), NOW() );
		
		SET i = i + 1;
		
	END WHILE;

END $ CALL proc_initData ();

这里需要使用到mysql的explain关键字,对count(1),count(*),count(id),count(name)分别查看性能

explain select count(1) from user
explain select count(*) from user
explain select count(id) from user
explain select count(name) from user

可以看出,select count(1)、count(*)、count(id)的执行计划是一毛一样的。多次执行取平均值,三者的性能也是非常趋近,因此可以认为三者性能相同。这里我加了个count(name)进行对比,并将最后30万条数据的name置空,可以看出性能有明显的差别。

mysql底层对count查询做了优化,当mysql确定count中的列名不为空时,实际上就是在统计行数。那么mysql内部会将count(列名)优化为count(*) —— 出自《高性能MySQL》一书

也就是说count(1)和count(主键字段)还是要优化到count(*)的,而如果只是统计某个列,只要该列不为空,无论是否为索引,都会被优化为count(*),因此三者性能并无任何差异。官方文档对其也进行了解释。

InnoDB handles SELECT COUNT(*) and SELECT COUNT(1) operations in the same way. There is no performance difference.

https://dev.mysql.com/doc/refman/5.7/en/group-by-functions.html#function_count

至此,事实上并未解决关于这个问题的疑惑。上面的内容都在强调“Mysql”,就是说,上面的验证可能仅对MySql有效,其他的数据库可能未必会对count语句进行优化。因此我又通过SQLServer去验证。

因为我本地并没有安装SQLServer,因此我是直接使用公司的开发库进行验证,这里不方便截图,直接说明一下200万条数据验证结果。

  1. 列名为主键,count(列名)会比count(1)快  
  2. 列名不为主键,count(1)会比count(列名)快  
  3. 如果表多个列并且没有主键,则 count(1) 的执行效率优于 count(*)  
  4. 如果有主键,则 select count(主键)的执行效率是最优的  
  5. 如果表只有一个字段,则 select count(*)最优。

可见,在SQLServer中,count(*)的性能并没有count(主键)高。

结语

根据上面对两个数据库的验证得出结论:不说是什么数据库都是耍流氓!SQL标准只提供了count这个内置函数,所有的数据库需要遵循这个标准,但是不同的数据库对于count的处理不同。在mysql中建议写count(*),而在SQLServer中建议写count(主键),在PostgreSql以及其他数据库中并未对其进行验证

### SQL COUNT 函数处理 NULL 值的区别 #### count(*) `count(*)` 返回表中的所有行数,不考虑任何列的具体值,包括含有 `NULL` 的行。这意味着无论某一行的某个特定字段是否为 `NULL`,只要该行存在就会被计入总数。 ```sql SELECT COUNT(*) FROM table_name; ``` 此操作不会受到具体列的影响,因为其关注的是整个记录的存在性而非某一列的内容[^3]。 #### count(1) 对于 `count(1)` 来说,实际上与 `count(*)` 是相同的;它同样会统计所有的行而不关心具体的列内容或是否存在 `NULL` 值。这是因为这里传递给 `count()` 的是一个常量表达式 (即数字 1),这使得每条记录都被视为有效并参与计数过程[^2]。 ```sql SELECT COUNT(1) FROM table_name; ``` 这种形式通常用于优化某些数据库引擎下的查询性能,但实际上两者的结果集完全一致。 #### count(主键) 当使用 `count(主键)` 进行计数时,由于主键本身不允许有重复项也不允许存储 `NULL` 值,因此理论上讲,除非删除了部分数据导致主键缺失,否则 `count(主键)` 应该等于 `count(*)` 或者 `count(1)`。然而,在实际应用中如果遇到违反唯一约束的情况,则可能会出现差异。 ```sql SELECT COUNT(primary_key_column) FROM table_name; ``` 需要注意的是,虽然主键不能包含 `NULL`,但在其他非主键列可能存在 `NULL` 值的情况下,通过这种方式可以间接验证是否有丢失的数据条目[^1]。 #### count(字段) 最后来看 `count(字段)`,这是最特殊的一种情况——只有当指定的字段不是 `NULL` 时才会对该行进行计数。换句话说,如果有任意数量的行在这个特定字段上有 `NULL` 值,那么这些行就不会出现在最终的统计数据之中。 ```sql SELECT COUNT(specific_column) FROM table_name WHERE specific_column IS NOT NULL; ``` 上述语句展示了如何排除掉那些在目标字段中含有 `NULL` 的行来进行更精确的数量统计[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值