MySQL 中 ISNULL() 函數與 IS NULL/IS NOT NULL 運算符易混淆,且關於「是否能用到索引」長期存在誤解。本文系統釐清函數與運算符差異、NULL 語義、索引利用機制、改寫優化策略與實戰測試結論。
| 形式 | 類型 | 語法 | 返回值 | 備註 |
|---|---|---|---|---|
ISNULL(expr) | 函數 | ISNULL(column) | 1 (NULL) / 0 (非NULL) | MySQL 專有,兼容性差 |
expr IS NULL | 運算符 | column IS NULL | TRUE / FALSE / UNKNOWN | SQL 標準,跨數據庫通用 |
expr IS NOT NULL | 運算符 | column IS NOT NULL | TRUE / FALSE / UNKNOWN | SQL 標準 |
expr = NULL | 比較運算 | column = NULL | 永遠為 UNKNOWN (即 FALSE) | 錯誤寫法! NULL 不參與比較 |
-- 正確查找 NULL
SELECT * FROM t WHERE name IS NULL;
SELECT * FROM t WHERE ISNULL(name); -- 同義,但非標準
-- 正確查找非 NULL
SELECT * FROM t WHERE name IS NOT NULL;
-- 錯誤:永遠返回空結果
SELECT * FROM t WHERE name = NULL; -- 永遠不匹配任何行
MySQL 可以對IS NULL和IS NOT NULL使用索引。優化器根據數據分佈、表大小、選擇性自動決定是否使用索引,**並非語法層面限制**。
-- 測試表:1025014 行,describle 列僅 1 行為 NULL,有索引
SELECT `id`,`title`,`describle` FROM bnsdh_table WHERE describle IS NOT NULL;
優化器兩種執行路徑權衡:
describle IS NOT NULL。describle 索引(不存 NULL 值的二級索引特性),獲取非 NULL 行的主鍵,再回表。決策依據: COST 估算。若非 NULL 行佔比極高(如 99.99%),全表掃描成本可能低於「索引掃描+大量回表」;若非 NULL 行佔比低,索引掃描更優。不是「不能用索引」,而是「優化器按成本選擇」。
-- 原查詢
SELECT click FROM bsga_table WHERE click IS NOT NULL;
-- 改寫為範圍查詢(假設 click 為正整數)
SELECT click FROM bsga_table WHERE click > 0 AND click < 100001;
結論: 改寫後**不一定更快**。若 click 有索引且數據分佈均勻,優化器對 IS NOT NULL 和範圍查詢均能正確選擇索引。盲目改寫反而增加維護成本。**核心優化點在於:確保列有索引、統計信息準確(ANALYZE TABLE)、避免隱式轉換導致索引失效。**
SELECT ISNULL(NULL); -- 1
SELECT ISNULL('abc'); -- 0
SELECT ISNULL(0); -- 0
SELECT ISNULL(''); -- 0 (空串非 NULL)
用途: 在選擇列表中轉換 NULL 顯示:
SELECT id, IFNULL(name, '未知') AS name, ISNULL(description) AS desc_is_null FROM articles;
IS NULL / IS NOT NULL,避免 ISNULL() 移植問題。IS NOT NULL 往往能走索引掃描。col = NULL / col <> NULL: 永遠不會命中任何行。ANALYZE TABLE,確保優化器成本估算準確。WHERE a IS NOT NULL AND b = ?,建立 INDEX (a, b) 可覆蓋查詢。IS NULL/IS NOT NULL 完全可以利用索引,關鍵在於優化器成本模型與數據分佈。拋棄「改寫為範圍查詢」的迷思,專注於:建正確索引、維護統計信息、用標準語法、EXPLAIN 驗證執行計劃,才是解決 NULL 過濾性能問題的正道。