没有性能差别。现代MySQL中COUNT(*)和COUNT(1)执行计划完全一致,优化器均等价转换为行数统计,不读字段值;COUNT(列名)才因需判NULL而可能变慢。
MySQL里
和
真有性能差别吗?
没有。在现代MySQL(5.7+,尤其是8.0)中,
和
生成的执行计划完全一致,优化器会把它们都等价转换为“统计行数”,不查具体字段值,也不触发表达式计算。
常见错误现象是看到老博客说“
更快因为不用解析
”,这源于对早期MySQL(如5.5之前)或某些存储引擎(如MyISAM)的过时理解。InnoDB下二者毫无区别。
是SQL标准语法,语义清晰:统计所有行(包括含NULL的行)
本质是
,优化器直接忽略常量值,只计数
哪怕写
或
,执行效果也一样
如果表有主键且走覆盖索引,
可能利用
快速估算(如
),但这和写1无关
什么情况下
才真正变慢?
只有当你用
或
时,行为才不同——它必须检查该列是否为
,跳过NULL值再计数。这才是实际影响性能的地方。
使用场景很明确:你需要的是“该列非NULL的行数”,而不是总行数。比如统计有手机号的用户数:
。
(id是主键)→ 必须逐行读取
字段判断是否NULL(虽然主键不可能NULL,但优化器不跳过检查)
→ 优化器可直接走聚簇索引的叶子节点计数,甚至用采样估算
如果
字段没索引,
会触发全表扫描+每行判NULL,比
明显慢
加了
约束且有索引的列,
可能被优化,但依然不如
稳定
PostgreSQL和SQL Server里还一样吗?
基本一样,但细节更值得盯住。
PostgreSQL中
是最快路径,优化器明确将其识别为“行计数”;而
需多一步表达式解析,虽开销微乎其微,但
里能看到极轻微的CPU差异(纳秒级)。SQL Server同理,
是首选,文档明确推荐。
PostgreSQL 14+ 对
做了并行聚合优化,
也能受益,但语义上仍是冗余
SQL Server里
可走
快速获取近似值(启用
时),
不行
Oracle 12c+ 中二者执行计划相同,但
在数据字典层面有特殊标记,更利于物化视图重写
别踩这个坑:误以为
能替代
不是
的等价写法,这是最隐蔽也最伤性能的误用。
比如你想查“是否存在订单金额>1000的用户”,写成
再判断结果是否>0,就错了——它会扫完所有匹配行;而
找到第一行就停。
→ 找到1行即返回true
→ 扫完全部匹配行,再比大小
当匹配行数大或表没合适索引时,性能差距可达百倍
即使加了
,
也无法提前终止(聚合函数必须收齐所有输入)
真正要注意的不是
还是
,而是你到底要“行数”还是“存在性”。前者看执行计划是否走了索引覆盖,后者必须用
或
子查询。其他都是幻觉。
COUNT(*)COUNT(1)COUNT(*)COUNT(1)COUNT(1)*COUNT(*)COUNT(1)COUNT(常量)COUNT(42)COUNT('hello')COUNT(*)index statisticsSHOW TABLE STATUSCOUNT(列名)COUNT(非空列)COUNT(可能为NULL的列)NULLCOUNT(phone)COUNT(id)idCOUNT(*)phoneCOUNT(phone)COUNT(*)NOT NULLCOUNT(列名)COUNT(*)COUNT(*)COUNT(1)EXPLAIN ANALYZECOUNT(*)COUNT(*)COUNT(1)COUNT(*)sys.dm_db_partition_statsREAD_UNCOMMITTEDCOUNT(1)COUNT(*)COUNT(*)EXISTSCOUNT(*) > 0EXISTSSELECT COUNT(*) FROM orders WHERE amount > 1000EXISTSEXISTS (SELECT 1 FROM orders WHERE amount > 1000)(SELECT COUNT(*) FROM orders WHERE amount > 1000) > 0LIMIT 1COUNT(*)*1EXISTSIN