第一步:技术解析与SQL示例
核心区别概述
-
• count(*): 统计所有行的数量。它不关心任何列的内容,只是简单地计算行数。它会包含值为NULL的行。
-
• count(1): 与count(*)基本等价。它为每一行计算一个常量1(一个非NULL值),然后统计结果集中有多少个1。它也会包含值为NULL的行。在现代MySQL(特别是InnoDB引擎)中,优化器会将count(1)和count(*)视为完全相同,性能上没有差别。
-
• count(列名): 统计指定列中值不为NULL的行的数量。它会忽略值为NULL的行。这是它与前两者最本质的功能区别。
SQL 示例
我们创建一张学生签到表,其中有些学生请假了(leave_reason 为 NULL)。
CREATE TABLE student_checkin (
id INTPRIMARY KEY AUTO_INCREMENT,
student_name VARCHAR(50),
checkin_time TIMESTAMP,
leave_reason VARCHAR(100) — 请假原因,未请假的学生此列为NULL
);
INSERT INTO student_checkin (student_name, checkin_time, leave_reason) VALUES
('Alice', NOW(), NULL),
('Bob', NOW(), 'Sick Leave'),
('Charlie', NOW(), NULL),
('David', NOW(), 'Personal Leave'),
('Eve', NOW(), NULL);
— 现在我们来执行三种不同的 COUNT 查询
SELECT
COUNT(*) AS total_rows_star,
COUNT(1) AS total_rows_one,
COUNT(leave_reason) AS non_null_leave_reasons
FROM
student_checkin;
查询结果:
total_rows_star |
total_rows_one |
non_null_leave_reasons |
5 |
5 |
2 |
结果解读:
-
• COUNT(*) 和 COUNT(1) 都返回了 5,因为它们统计的是表中的总行数,不管任何列的值是什么。
-
• COUNT(leave_reason) 返回了 2,因为它只统计了 leave_reason 这一列值不为NULL的行数(只有Bob和David请假了)。
故事场景:班主任的三种点名方式
你是一位班主任,需要统计班级(数据表)的出勤情况。
方式一:COUNT(*) — “挨个点人头”
这是最直接、最原始的统计方式。
-
• 老师的操作:
老师站在讲台上,想知道“今天教室里一共有多少个学生?”。他伸出手指,从第一排开始,挨个指向每一个学生(每一行数据),嘴里数着:“1, 2, 3, 4, 5…”。 -
• 特点:
老师不关心学生叫什么,也不关心他是否在打瞌睡。只要这个座位上有一个人,他就会被计入总数。这种方式统计的是物理存在的总人数。
方式二:COUNT(1) — “划正字计数”
这是一种看起来不同,但结果完全一样的方式。
-
• 老师的操作:
这次老师换了一种方法。他看着教室里的学生,每看到一个学生,就在黑板上划一个“正”字的一笔(常量1)。最后,他数黑板上有多少笔画,就知道总人数了。 -
• 与COUNT(*)的“历史恩怨”:
很久以前,有些老教师认为“指一下学生全身(*)”这个动作,比在黑板上“简单划一笔(1)”要慢。但在现代化的“智慧教室”(现代MySQL优化器)里,系统早就明白,这两种方式的核心都是“看一眼学生”,所以它们的效率是完全一样的。COUNT(1) 和 COUNT(*) 性能没有区别,COUNT(*) 是SQL标准里更推荐的写法。
方式三:COUNT(列名) — “交了作业的请举手!”
这是一种带有筛选条件的统计方式。
-
• 老师的操作:
今天老师的目的变了。他不想知道总人数,只想知道“今天有多少人交了作业?”(COUNT(homework_status))。
他朝台下喊道:“所有交了作业的同学,请举手!” -
• 特点:
-
• 那些没交作业的学生(homework_status 字段为 NULL),会心虚地低下头,绝不会举手。
-
• 老师最终统计的是举手的数量。
-
-
• 结论:
这种方式统计的不是班级的总人数,而是**满足特定条件(指定列的值不为空)**的人数。
故事总结:
聚合函数 |
COUNT(*) (点人头) | COUNT(1) (划正字) | COUNT(列名) (举手点名) |
统计目标 | 总行数 | 总行数 | 指定列中非NULL值的行数 |
处理NULL |
✅ 计算在内 |
✅ 计算在内 |
❌ 忽略 |
性能 | 高
(官方推荐) |
高
(与COUNT(*)相同) |
可能较低
(需额外判断NULL) |
核心比喻 | 挨个点名,统计总人数 | 划正字,统计总人数 | 举手点名,统计交作业的人数 |
一句话总结 | 我数的是“行”。 | 我和楼上一样。 | 我数的是“特定列不为空的行”。 |
最终建议:
-
• 当你想统计表的总行数时,请使用 COUNT(*)。这是最规范、最清晰、性能也最好的选择。
-
• 只有当你的需求是统计某一列中有效值(非NULL)的数量时,才使用 COUNT(列名)。
-
• COUNT(1) 只是 COUNT(*) 的一个等价写法,为了代码的清晰和标准化,推荐使用COUNT(*)。
评论前必须登录!
注册