CodeGym /課程 /SQL SELF /NULL 對聚合函數的影響:SUM(), COUNT(), AVG(), MIN(), MAX()

NULL 對聚合函數的影響:SUM(), COUNT(), AVG(), MIN(), MAX()

SQL SELF
等級 9 , 課堂 2
開放

來回顧一下,聚合函數就是那種一次處理多行資料,然後回傳一個結果的函數。在 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

現在我們來下幾個查詢,看看結果:

  1. 所有分數加總:SUM()
SELECT SUM(score) AS total_score
FROM students_scores;

結果:

total_score
251

你看,缺漏的 NULL 完全沒被加進去。愛麗絲 (85)、查理 (92)、葉蓮娜 (74) 加起來就是 251。鮑勃跟達娜就沒算進來。

  1. 平均分數:AVG()
SELECT AVG(score) AS average_score
FROM students_scores;

結果:

average_score
83.67

一樣,NULL 被忽略,平均只算有分數的:(85 + 92 + 74) / 3 = 83.67

  1. 最小和最大分數: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。

  1. 行數計算:COUNT(*) vs COUNT(column)
SELECT
    COUNT(*) AS total_rows, 
    COUNT(score) AS non_null_scores 
FROM students_scores;

結果:

total_rows non_null_scores
5 3
  • COUNT(*) 算了所有行,就算 scoreNULL 也算。
  • 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 的小撇步

  1. 根據需求決定要不要算 NULL 你要搞清楚查詢時 NULL 要不要算進去。有時像 AVG(),忽略它們才對。有時像算總數,NULL 也要算。
  2. 需要時用 COALESCE() 如果你要把 NULL 換成預設值來算,用 COALESCE() 就對了(這個下堂會講)。
  3. 別搞混 COUNT(*)COUNT(column) 這是新手最常犯的錯。前者算所有行,後者只算有值的行。

現在你知道這個很會裝死的 NULL 怎麼影響聚合了。這樣你就不會被它陰了,還能善用 NULL。下堂我們會學超好用的 COALESCE(),讓你處理 NULL 更順手!

留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION