來回顧一下,聚合函數就是那種一次處理多行資料,然後回傳一個結果的函數。在 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 | 魔杖 | 150 |
| 2 | 魔法斗篷 | NULL |
| 3 | 藥水瓶 | 75 |
| 4 | 咒語書 | 200 |
| 5 | 水晶球 | 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。 你要搞清楚查詢時NULL要不要算進去。有時像AVG(),忽略它們才對。有時像算總數,NULL也要算。 - 需要時用
COALESCE()。 如果你要把NULL換成預設值來算,用COALESCE()就對了(這個下堂會講)。 - 別搞混
COUNT(*)跟COUNT(column)。 這是新手最常犯的錯。前者算所有行,後者只算有值的行。
現在你知道這個很會裝死的 NULL 怎麼影響聚合了。這樣你就不會被它陰了,還能善用 NULL。下堂我們會學超好用的 COALESCE(),讓你處理 NULL 更順手!
GO TO FULL VERSION