回忆一下,聚合函数就是那种能一次性处理多行数据并返回一个结果的函数。在 PostgreSQL 里你经常会用到这些聚合函数:
SUM()— 求和。AVG()— 求平均值。MIN()— 找最小值。MAX()— 找最大值。COUNT()— 统计行数。
乍一看很简单:把列或者表达式丢进函数里,直接拿结果。但如果列里有 NULL 呢?
NULL 在聚合函数里的表现:快速了解
这时候就有意思了:
SUM()和AVG()会忽略NULL。只要有一条记录是NULL,它就不会参与计算。挺合理的,毕竟如果有人“没来参加聚会”,总和怎么会变?平均值也一样,缺一个数怎么算?MIN()和MAX()也会跳过NULL。它们只会在不是NULL的数据里找最小或最大。所以你找最年轻的员工时,如果有人没填生日,NULL不会被选中。COUNT(*)会统计所有行,包括有NULL的。而COUNT(column)只会统计指定列有值的行,也就是NULL会被忽略。
我们来用例子看看。
聚合函数和 NULL 的用法例子
这是 students_scores 表,存着学生的测试分数:
| student_id | name | score |
|---|---|---|
| 1 | 阿丽萨 | 85 |
| 2 | 鲍勃 | NULL |
| 3 | 查理 | 92 |
| 4 | 达娜 | NULL |
| 5 | 叶莲娜 | 74 |
现在我们写几个查询,看看结果:
- 所有分数的总和:
SUM()
SELECT SUM(score) AS total_score
FROM students_scores;
结果:
| total_score |
|---|
| 251 |
你看,缺失的 NULL 分数根本没参与求和。阿丽萨 (85)、查理 (92)、叶莲娜 (74) 的分数加起来就是 251。鲍勃和达娜就被跳过了。
- 平均分:
AVG()
SELECT AVG(score) AS average_score
FROM students_scores;
结果:
| average_score |
|---|
| 83.67 |
同样,NULL 被无视了,平均值只算有分数的:(85 + 92 + 74) / 3 = 83.67。
- 最小分和最高分:
MIN()和MAX()
SELECT
MIN(score) AS min_score,
MAX(score) AS max_score
FROM students_scores;
结果:
| min_score | max_score |
|---|---|
| 74 | 92 |
这里也一样:NULL 还是被忽略,最小分是 74,最大分是 92。
- 统计行数:
COUNT(*)vsCOUNT(column)
SELECT
COUNT(*) AS total_rows,
COUNT(score) AS non_null_scores
FROM students_scores;
结果:
| total_rows | non_null_scores |
|---|---|
| 5 | 3 |
COUNT(*)统计了所有行,包括score是NULL的。COUNT(score)只算score有值的行。
实用案例
来几个实际点的例子。
例子 1:统计有工资和没工资的员工数量
假设我们有个 employees 表,里面有工资。
| id | name | salary |
|---|---|---|
| 1 | Alex Lin | 50000 |
| 2 | Maria Chi | NULL |
| 3 | Anna Song | 60000 |
| 4 | Otto Art | NULL |
| 5 | Liam Park | 55000 |
我们想知道有多少员工填了工资,有多少没填。
SELECT
COUNT(*) AS total_employees,
COUNT(salary) AS employees_with_salary,
COUNT(*) - COUNT(salary) AS employees_without_salary
FROM employees;
这里:
COUNT(*)返回员工总数。COUNT(salary)统计填了工资的员工数。- 没填工资的数量就是两者相减。
结果
| total_employees | employees_with_salary | employees_without_salary |
|---|---|---|
| 5 | 3 | 2 |
例子 2:计算商品平均价格,考虑缺失数据
你是魔法商店老板,products 表里有 price 列,但有些商品还没定价。
| id | name | price |
|---|---|---|
| 1 | Magic Wand | 150 |
| 2 | Enchanted Cloak | NULL |
| 3 | Potion Bottle | 75 |
| 4 | Spell Book | 200 |
| 5 | Crystal Ball | NULL |
你只想算有价格的商品的平均价。
SELECT AVG(price) AS average_price
FROM products;
结果:
| average_price |
|---|
| 141.6667 |
如果你想给没定价的商品设个默认价(比如 0),可以用 COALESCE(),这个我们下节课讲。
例子 3:找出学生的最小和最大年龄
students 表里有学生年龄,但有些学生年龄未知(NULL)。
| id | name | age |
|---|---|---|
| 1 | Alex Lin | 20 |
| 2 | Maria Chi | NULL |
| 3 | Anna Song | 19 |
| 4 | Otto Art | 22 |
| 5 | Liam Park | NULL |
我们想知道最小和最大的学生年龄。
SELECT
MIN(age) AS youngest_student,
MAX(age) AS eldest_student
FROM students;
结果:
| youngest_student | eldest_student |
|---|---|
| 19 | 22 |
这个查询只会返回有年龄的学生的最小和最大年龄。NULL 还是被跳过了。
注意事项和坑点
用聚合函数处理 NULL 时,记住这些:
- 在
SUM()和AVG()里,NULL不参与计算。这样你就不会把“空值”算进去。 - 如果你想统计有
NULL的行,用COUNT(*)。 - 用
MIN()或MAX()时,NULL不影响结果。但如果整列全是NULL,结果也是NULL。
和 NULL 打交道的小贴士
- 根据实际需求来。 你得搞清楚你的查询到底要不要考虑
NULL。有时候像AVG()这样忽略NULL正合适。有时候比如统计总数,NULL也得算进去。 - 需要时用
COALESCE()。 如果你想在计算时把NULL换成默认值,COALESCE()就是你的好帮手(不过这个下节课再说)。 - 别搞混
COUNT(*)和COUNT(column)。 这是新手最容易犯的错。前者统计所有行,后者只算有值的行。
现在你知道了,NULL 这个“沉默的家伙”会怎么影响聚合函数。掌握了这些,你就能避免踩坑,还能用 NULL 玩出花来。下节课我们会学一个强大的工具 COALESCE(),让你处理 NULL 更溜!
GO TO FULL VERSION