你还在认为 count(1) 比 count(*) 效率高?

本文详细解释了SQL中的count(*)、count(1)和count(列名)的区别,包括它们在不同情况下的执行效果和效率对比,帮助读者理解何时使用何种计数函数更合适。

1. count(1) and count(*)

当表的数据量大些时,对表作分析之后,使用count(1)还要比使用count(*)用时多了!
从执行计划来看,count(1)和count(*)的效果是一样的,但是在表做过分析之后,count(1)会比count(*)的用时少些(1w以内数据量),不过差不了多少。
如果count(1)是聚索引 id,那肯定是count(1)快,但是差的很小的。
因为count(*),自动会优化指定到那一个字段。所以没必要去count(1),用count(*),sql会帮你完成优化的。
因此:count(1)和count(*)基本没有差别!

2. count(1) and count(字段)

(1) count(1) 会统计表中的所有的记录数,包含字段为null 的记录。
(2) count(字段) 会统计该字段在表中出现的次数,忽略字段为null 的情况。即不统计字段为null 的记录。

3. count(*) 和 count(1)和count(列名)区别

执行效果上:
count(*)包括了所有的列,相当于行数,在统计结果的时候,不会忽略列值为NULL。
count(1)包括了忽略所有列,用1代表代码行,在统计结果的时候,不会忽略列值为NULL。
count(列名)只包括列名那一列,在统计结果的时候,会忽略列值为空(这里的空不是只空字符串或者0,而是表示null)的计数,即某个字段值为NULL时,不统计。
执行效率上:
列名为主键,count(列名)会比count(1)快。
列名不为主键,count(1)会比count(列名)快。
如果表多个列并且没有主键,则 count(1) 的执行效率优于 count(*)。
如果有主键,则 select count(主键)的执行效率是最优的。
如果表只有一个字段,则 select count(*)最优。

<think> 我们正在讨论COUNT(1)COUNT(主键)COUNT(*)MySQL中的效率问题。根据引用内容,我们可以总结如下: 引用[1]中提到在MySQL 8.0.27的InnoDB引擎下,使用1200万测试数据表进行测试,测试了COUNT(*)等。 引用[2]指出: - 当列名是主键时,COUNT(列名)COUNT(1)。 - 当列名不是主键时,COUNT(1)COUNT(列名)。 - 如果表有多个列且没有主键,则COUNT(1)的效率优于COUNT(*)。 - 如果有主键,则COUNT(主键)的执行效率最优。 - 如果表只有一个字段,则COUNT(*)最优。 引用[3]提到,当数据量大时,对表进行分析后,COUNT(1)可能比COUNT(*)用时稍多(在1万以内数据量时,相差不大),并且如果COUNT(1)使用了聚集索引(比如主键),那么它会一点,但差距很小。 引用[4]则说明了现代数据库引擎(如InnoDB)对COUNT(*)做了优化,使其效率接近COUNT(1)。在MyISAM引擎中,COUNT(*)会直接读取一个存储的总行数值,因此非常,而InnoDB由于MVCC机制,需要遍历表来计数(除非使用某些优化,如二级索引计数)。 综合以上引用,我们可以得出结论: 1. 在InnoDB引擎中: - `COUNT(*)`和`COUNT(1)`的效率非常接近,因为优化器对它们做了类似的处理(都是统计行数,不关心具体列的值)。 - `COUNT(主键)`:如果使用主键字段,那么InnoDB会遍历主键索引(聚簇索引),因为主键索引的叶子节点包含了完整行数据,但计数时只需要主键(非NULL),所以效率也很高。但是,由于主键索引通常比二级索引大,遍历主键索引可能比遍历二级索引慢(如果有可用的二级索引且小于主键索引)。 2. 实际上,在InnoDB中,优化器会选择最小的二级索引来统计行数(因为二级索引的叶子节点只包含索引列和主键,比聚簇索引小),如果没有二级索引,则使用主键索引。 3. 因此,在InnoDB引擎下,三者的效率比较如下(在大多数情况下): - 如果存在可用的二级索引(非主键索引),那么`COUNT(*)`和`COUNT(1)`会使用该二级索引进行扫描,此时效率高于`COUNT(主键)`(因为主键索引通常更大)。 - 如果没有二级索引,则`COUNT(*)`、`COUNT(1)`和`COUNT(主键)`都会扫描主键索引,此时效率相当。 4. 特别说明: - `COUNT(主键)`:InnoDB需要一行行读取主键,并跳过NULL值(主键不可能为NULL,所以实际上就是全表行数)。 - `COUNT(1)`:InnoDB会遍历最小的可用索引(通常是某个二级索引),因为不需要取具体的列值,所以只需要扫描索引结构。 - `COUNT(*)`:在MySQL中,`COUNT(*)`是特例,它直接统计行数,不关心列值,因此优化器会选择最小的索引进行扫描。 所以,在InnoDB中,通常: `COUNT(*)` ≈ `COUNT(1)` >= `COUNT(主键)` 注意:这里`COUNT(*)`和`COUNT(1)`基本相当,而`COUNT(主键)`可能稍慢,因为主键索引通常比二级索引大(如果有二级索引可用的话)。 如果没有二级索引,那么三者都会扫描主键索引,效率相近。 另外,引用[4]提到,在MyISAM引擎中,`COUNT(*)`会直接返回存储的行数,速度极(常量时间),而其他两种方式(包括`COUNT(1)`)则不行,因为MyISAM对于不带WHERE条件的`COUNT(*)`有特殊优化。但在InnoDB中,由于事务特性(MVCC),不能直接存储一个总行数,所以需要扫描。 因此,对于InnoDB表,推荐使用`COUNT(*)`,因为它语义清晰(统计行数),并且优化器会尽量选择最优的索引进行扫描。 总结: - 在InnoDB中,优先使用`COUNT(*)`。 - `COUNT(*)`和`COUNT(1)`效率相当,且通常比`COUNT(主键)`(如果有二级索引)。 - 如果没有二级索引,则`COUNT(*)`、`COUNT(1)`和`COUNT(主键)`效率相近。 下面我们用公式表示执行过程: 设: - $t$ 为表 - $pk$ 为主键索引 - $idx$ 为任意二级索引(非主键索引) 则: - $\text{COUNT(*)}$ 的执行成本:$\min( \text{size}(idx_i) )$,即选择最小的二级索引,如果没有则使用$pk$。 - $\text{COUNT(1)}$ 的执行成本:同上。 - $\text{COUNT(pk)}$ 的执行成本:$\text{size}(pk)$(因为必须扫描主键索引)。 因此,当存在一个二级索引$idx$满足$\text{size}(idx) < \text{size}(pk)$时,则: $$ \text{COUNT(*)COUNT(1) 的效率} > \text{COUNT(pk)}$$ --- ### 结论 在MySQL InnoDB引擎中: 1. `COUNT(*)`和`COUNT(1)`的执行效率几乎相同,优化器会选择最小的索引进行扫描[^4]。 2. `COUNT(主键)`需要扫描主键索引(聚簇索引),如果主键索引大于二级索引,则效率低于前两者[^2]。 3. 若无二级索引,三者效率相近。 因此,**推荐使用`COUNT(*)`**,语义明确且效率最高[^4]。 --- §§相关问题§§ 1. 为什么InnoDB的`COUNT(*)`不能像MyISAM那样直接返回存储的行数? 2. 如何优化大表的`COUNT`查询性能? 3. 除了`COUNT`,还有哪些方法可以获取表行数? </think> 在 MySQL InnoDB 引擎中,`COUNT(1)`、`COUNT(主键)` 和 `COUNT(*)` 的效率对比可总结如下(基于测试数据和引擎优化): ### 📊 效率对比结论 1. **`COUNT(*)`(推荐首选)** - 现代 MySQL 版本(如 8.0+)对其进行了深度优化 - **执行逻辑**:直接统计行数,不解析具体列值 - **效率**:接近 `COUNT(1)`,在大部分场景中性能最优[^4] - **适用场景**:通用行数统计,尤其表结构简单时[^2] 2. **`COUNT(1)`** - **执行逻辑**:统计每行常量值 `1`,不读取实际列数据 - **效率**:与 `COUNT(*)` 几乎相同,微小差异可忽略[^3][^4] - **注意事项**:数据量小时可能略慢于 `COUNT(*)`(万级以内)[^3] 3. **`COUNT(主键)`** - **执行逻辑**:遍历主键索引(聚簇索引),跳过 NULL 值 - **效率对比**: - 若主键是整型:速度最(因主键索引最小)[^2] - 若主键非整型(如 UUID):可能慢于 `COUNT(*)`(索引更大) - **适用场景**:明确需要主键统计时 --- ### ⚙️ 性能关键因素 1. **索引类型** - 主键索引(聚簇索引)效率高于普通索引 - 公式:扫描成本 $C \propto \text{索引大小}$ ```sql -- 主键索引最小 → COUNT(主键) CREATE TABLE t (id INT PRIMARY KEY, name VARCHAR(20)); ``` 2. **表引擎差异** - **InnoDB**:需遍历索引(因 MVCC 事务支持) - **MyISAM**:`COUNT(*)` 直接读取元数据(常量时间)[^4] 3. **数据量和表结构** - 单列表:`COUNT(*)` 最优[^2] - 多列无主键表:`COUNT(1)` 略优[^2] --- ### ✅ 最终建议 | 场景 | 推荐写法 | 原因 | |-------------------------------|---------------|-------------------------------| | 通用行数统计 | `SELECT COUNT(*)` | 语义明确,优化充分[^4] | | 需主键统计 | `COUNT(主键)` | 避免 NULL 检查 | | 兼容旧版本(< MySQL 5.7) | `COUNT(1)` | 规避历史版本优化问题 | > 💡 **性能差异提示**:在 1200 万数据测试中,三者差异通常在毫秒级[^1],无需过度优化。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值