MySQL 條件查詢:IS NULL 與 NOT NULL
簡介
在資料庫的日常操作中,NULL 扮演著「未知」或「缺失」的角色。與一般的數值、字串不同,NULL 不能直接以 =、!= 來比較,必須使用專門的運算子 IS NULL 或 IS NOT NULL 來判斷欄位是否為空。
如果對 NULL 的處理不夠熟悉,常會出現查詢結果不如預期、資料統計錯誤,甚至產生隱蔽的程式錯誤。因而掌握 IS NULL/IS NOT NULL 的正確用法,是每位 MySQL 使用者在條件查詢階段的必備功力。
本篇文章將從概念說明、實作範例、常見陷阱與最佳實踐,以及實務應用場景四個面向,完整闡述如何在 MySQL 中安全、有效地使用 IS NULL 與 NOT NULL。本文適合 初學者到中級開發者,即使你僅會簡單的 SELECT,也能快速上手。
核心概念
1. 為什麼不能用 = NULL
在 SQL 標準裡,NULL 代表「未知」的值,任何與未知值的比較結果都是 UNKNOWN(三值邏輯:TRUE、FALSE、UNKNOWN)。因此:
SELECT * FROM users WHERE email = NULL; -- 永遠不會回傳資料
上述語句永遠不會符合條件,因為 email = NULL 的結果是 UNKNOWN,而不是 TRUE。正確的寫法必須改用 IS NULL:
SELECT * FROM users WHERE email IS NULL;
2. IS NULL 與 IS NOT NULL 的語法
| 運算子 | 語意 | 範例 |
|---|---|---|
IS NULL |
判斷欄位值是否為 NULL | col IS NULL |
IS NOT NULL |
判斷欄位值是否 非 NULL | col IS NOT NULL |
注意:
IS NOT NULL與NOT (col IS NULL)等價,但前者可讀性更佳,且在優化器上表現更好。
3. NULL 與索引的互動
- 普通 B‑Tree 索引:
IS NULL可以使用索引(自 MySQL 5.7 起支援),但IS NOT NULL仍會走全表掃描,除非同時加入其他篩選條件。 - 覆寫索引(Composite Index):若索引欄位中包含
NULL,MySQL 仍會把NULL作為索引鍵的一部份,允許透過IS NULL直接定位。
4. NULL 與聚合函式
聚合函式(COUNT()、SUM()、AVG())會自動忽略 NULL。若想計算 NULL 的筆數,必須配合 IS NULL:
SELECT COUNT(*) AS total,
COUNT(col) AS not_null,
SUM(col IS NULL) AS null_cnt
FROM table_name;
SUM(col IS NULL) 會把 TRUE(值 1)累加,得到 NULL 的筆數。
程式碼範例
以下示範 5 個常見情境,均以 employees 資料表為例(欄位:id、name、department_id、email、hire_date)。
範例 1:找出尚未填寫 Email 的員工
-- 只要 email 為 NULL 的列都會被挑出
SELECT id, name, hire_date
FROM employees
WHERE email IS NULL;
說明:
IS NULL直接檢查欄位是否缺值,適合資料清理或提醒使用者補全資訊。
範例 2:找出已指定部門且部門編號非空的員工
SELECT id, name, department_id
FROM employees
WHERE department_id IS NOT NULL;
說明:
IS NOT NULL可快速過濾掉「未分配部門」的資料;若再加上department_id > 0,可避免意外的0或負值。
範例 3:結合 IS NULL 與其他條件(複合查詢)
-- 同時滿足以下兩個條件:
-- 1. email 為 NULL
-- 2. 入職日期在 2023 年以後
SELECT id, name, hire_date
FROM employees
WHERE email IS NULL
AND hire_date > '2023-01-01';
說明:
AND結合多個條件時,MySQL 會先利用索引(若有)過濾hire_date,再檢查email IS NULL,提升效能。
範例 4:使用 IS NULL 在 CASE 表達式中產生標記
SELECT id,
name,
CASE
WHEN email IS NULL THEN '未填寫 Email'
ELSE '已提供 Email'
END AS email_status
FROM employees;
說明:在報表或前端顯示時,
CASE讓 NULL 與非 NULL 產生不同的文字描述,提升可讀性。
範例 5:計算每個部門的 NULL Email 數量
SELECT department_id,
COUNT(*) AS total_emp,
SUM(email IS NULL) AS null_email_cnt
FROM employees
GROUP BY department_id;
說明:
SUM(email IS NULL)會把每筆email IS NULL為 TRUE 的列加 1,直接得到缺失的筆數,避免額外的子查詢。
常見陷阱與最佳實踐
| 陷阱 | 可能產生的問題 | 解決方法 / 最佳實踐 |
|---|---|---|
使用 = 或 <> 判斷 NULL |
查詢永遠回傳空集合 | 必須改用 IS NULL / IS NOT NULL |
| 忘記考慮三值邏輯 | WHERE col <> 5 會把 NULL 排除,可能誤以為「不等於 5」的結果包含 NULL |
若要同時排除 NULL,寫成 WHERE col <> 5 AND col IS NOT NULL |
在 WHERE 中混用 NULL 與函式 |
WHERE COALESCE(col, 0) = 0 會把原本的 NULL 變成 0,影響統計 |
直接使用 IS NULL,或明確說明 COALESCE 的意圖 |
| 索引失效 | WHERE NOT (col IS NULL) 可能導致全表掃描 |
盡量寫成 WHERE col IS NOT NULL,必要時加上其他可使用索引的條件 |
| 聚合時忽略 NULL | AVG(salary) 不會把 NULL 當作 0,可能低估平均值 |
若需要把 NULL 視為 0,使用 AVG(COALESCE(salary,0));若想知道有多少 NULL,另寫 SUM(salary IS NULL) |
最佳實踐小結
- 永遠使用
IS NULL/IS NOT NULL取代=、!=。 - 結合索引:在可行的情況下,把
IS NULL放在索引欄位上,或同時加上其他索引條件。 - 明確三值邏輯:在需要排除 NULL 時,務必加上
col IS NOT NULL。 - 利用
CASE/COALESCE:在輸出層面把 NULL 轉換成易讀文字或預設值。 - 測試與驗證:使用
EXPLAIN檢視查詢計畫,確保不會因為 NULL 判斷而產生不必要的全表掃描。
實際應用場景
使用者資料完整度檢查
- 定期跑
SELECT * FROM users WHERE phone IS NULL OR address IS NULL;,找出缺失欄位,發送提醒郵件給使用者。
- 定期跑
報表與儀表板
- 在銷售報表中,
SUM(order_amount IS NULL)可快速顯示「尚未填寫金額」的訂單筆數,協助業務團隊追蹤資料落差。
- 在銷售報表中,
資料遷移與清理
- 在將舊系統資料搬入新資料庫前,先用
SELECT * FROM old_table WHERE critical_col IS NULL;找出必須補齊的關鍵欄位,避免因為 NULL 而觸發唯一鍵衝突。
- 在將舊系統資料搬入新資料庫前,先用
條件授權
- 某些功能只對已完成驗證的帳號開放:
WHERE email_verified_at IS NOT NULL。
- 某些功能只對已完成驗證的帳號開放:
多表 JOIN 的排除
- 在左連接(LEFT JOIN)後,若需要找出「在左表有,但右表沒有對應資料」的列,可使用
WHERE right_table.id IS NULL來判斷。
- 在左連接(LEFT JOIN)後,若需要找出「在左表有,但右表沒有對應資料」的列,可使用
總結
- NULL 不是資料,而是「未知」或「缺失」的狀態,必須使用
IS NULL/IS NOT NULL來正確判斷。 - 了解 三值邏輯、索引行為以及聚合函式對 NULL 的處理方式,可避免常見的查詢錯誤與效能問題。
- 透過 案例、最佳實踐 與 實務場景,你可以在日常的 MySQL 開發與維運中,快速定位缺失資料、提升報表品質、降低系統錯誤率。
掌握了 IS NULL 與 NOT NULL 的正確使用,你的資料庫查詢將變得更可靠、可讀、高效。祝你在 MySQL 的旅程中,持續寫出乾淨、正確的 SQL!