MySQL 中 count(1)、count(*) 与 count(列名) 的区别?

三种 COUNT 方式的语义对比

写法 统计内容 NULL 值处理 执行效率
count(*) 统计 总行数 不忽略 NULL ⭐⭐⭐⭐⭐ 最优(MySQL 优化)
count(1) 统计 总行数 不忽略 NULL ⭐⭐⭐⭐ 等同于 count(*)
count(列名) 统计 该列非 NULL 的行数 忽略 NULL ⭐⭐⭐ 需要判断列值

一句话总结count(*) 和 count(1) 效果相同,统计所有行;count(列名) 只统计该列 非 NULL 的行数。推荐使用 count(*)

深度解析

一、核心区别:NULL 值处理

这是三种写法最本质的区别:

-- 测试表
CREATE TABLE user (
    id INT PRIMARY KEY,
    name VARCHAR(50),
    age INT
);

INSERT INTO user VALUES (1, 'Alice', 25);
INSERT INTO user VALUES (2, 'Bob', NULL);      -- age 为 NULL
INSERT INTO user VALUES (3, NULL, 30);         -- name 为 NULL
INSERT INTO user VALUES (4, NULL, NULL);       -- 两个都是 NULL

-- 查询对比
SELECT count(*) FROM user;      -- 结果:4(统计所有行)
SELECT count(1) FROM user;      -- 结果:4(统计所有行)
SELECT count(name) FROM user;   -- 结果:2(只有 Alice、Bob 的 name 非 NULL)
SELECT count(age) FROM user;    -- 结果:2(只有 Alice、Carol 的 age 非 NULL)

MySQL 中 count(1)、count(*) 与 count(列名) 的区别?

上图展示了三种 COUNT 方式对 NULL 值的处理差异。核心要点:

  • count(*):统计表的 总行数,不管列值是否为 NULL
  • count(1):语义上是 “统计每一行 1 这个表达式非 NULL 的数量”,实际上 MySQL 会优化成和 count(*) 一样
  • count(列名):统计该列 非 NULL 值的行数,会跳过 NULL 值

二、执行效率对比

流传的误区:很多人认为 count(1) 比 count(*) 快,因为 count(*) 会扫描所有列。

实际情况:在现代 MySQL(5.7+)中,两者效率 完全相同,甚至 count(*) 更优。

MySQL 中 count(1)、count(*) 与 count(列名) 的区别?

为什么 count(*) 更快?

  • MySQL 专门优化:InnoDB 对 count(*) 做了特殊优化,会自动选择 最小的辅助索引 进行扫描
  • 不扫描全部列count(*) 不会读取所有列数据,只统计行数
  • count(1) 会被优化成 count(*),两者执行计划完全相同

三、为什么 InnoDB 的 count(*) 这么慢?

很多人发现 InnoDB 的 count(*) 比 MyISAM 慢很多,这是因为:

MySQL 中 count(1)、count(*) 与 count(列名) 的区别?

四、大表 count 优化方案

对于 InnoDB 大表的 count 查询,可以考虑以下优化方案:

方案一:使用缓存

// 使用 Redis 缓存总数
public long getUserCount() {
    String count = redis.get("user:count");
    if (count != null) {
        return Long.parseLong(count);
    }

    // 缓存不存在,查询数据库并缓存
    long cnt = userMapper.selectCount(null);
    redis.set("user:count", String.valueOf(cnt), 300); // 缓存 5 分钟
    return cnt;
}

方案二:维护计数表

-- 创建计数表
CREATE TABLE table_counts (
    table_name VARCHAR(50) PRIMARY KEY,
    row_count BIGINT
);

-- 通过触发器或业务代码维护计数
INSERT INTO table_counts VALUES ('user', 0);
UPDATE table_counts SET row_count = row_count + 1 WHERE table_name = 'user';

方案三:估算(不要求精确)

-- 使用 EXPLAIN 估算
EXPLAIN SELECT * FROM user;

-- 查看 rows 列,是估算值
-- 适用于不要求精确计数的场景

五、使用场景建议

场景 推荐写法 原因
统计总行数 count(*) 语义清晰、MySQL 优化、推荐写法
统计某列非空数量 count(列名) 语义明确,会忽略 NULL
联合统计 count(DISTINCT 列名) 统计去重后的非 NULL 数量
条件统计 SUM(CASE WHEN 条件 THEN 1 ELSE 0 END) 更灵活的条件统计
-- 推荐写法
SELECT count(*) FROM user;                    -- 统计总行数
SELECT count(*) FROM user WHERE age > 18;     -- 条件统计

-- 特定场景
SELECT count(email) FROM user;                -- 统计有邮箱的用户数
SELECT count(DISTINCT age) FROM user;         -- 统计不同年龄的数量

-- 复杂条件统计
SELECT
    count(*) as total,
    SUM(CASE WHEN age > 18 THEN 1 ELSE 0 END) as adult,
    SUM(CASE WHEN gender = 'F' THEN 1 ELSE 0 END) as female
FROM user;
上一篇 kingbase sys_dump使用详解
下一篇 32岁阿里P7,把简历改成不知名小公司,学历改成普通本科,工作内容不变,投简历全挂!

站点性能

运行正常
实时心跳0 ms
页面加载
0
SQL 查询
0
服务端响应
0 ms
峰值内存
0 MB
阿陶学长

阿陶学长管理员

专注于分享最有价值的互联网干货

本月创作热力图

最新评论
暂无评论
AeroCore图片