NULL 與空值的混淆是導致數據質量問題、查詢邏輯錯誤、索引失效的核心原因。本文從存儲原理、性能影響、查詢語義三個維度徹底釐清。
| 概念 | 本質 | 佔用空間 | 語義 | 示例 |
|---|---|---|---|---|
| NULL | 特殊標記,表示「值未知/不存在」 | 是 (MyISAM: 1bit/列,InnoDB: 額外字節) | 不知情、不適用、未填寫 | 用戶未設置生日 |
| 空字符串 '' | 長度為 0 的字符串 | 是 (VARCHAR: 1-2字節長度前綴) | 明確知道、但內容為空 | 用戶填寫了備註但刪空了 |
| 數值 0 | 整數零值 | 是 (按類型固定字節) | 明確的零值 | 商品價格 0 元 (免費) |
.MYD 數據文件中直接標記COMPACT/REDUNDANT:NULL 列不佔用數據區空間,但位圖固定開銷DYNAMIC/COMPRESSED:長列溢出頁面不存 NULLWHERE col IS NULL 無法走索引 (全表掃描)ANALYZE TABLE 統計時 NULL 不計入基數,可能導致優化器誤判IS NULL / IS NOT NULL 可能無法使用範圍優化COUNT(col) 不計算 NULL,COUNT(*) 計算所有行ORDER BY col:NULL 排在最前 (ASC) 或最後 (DESC)CHAR(10):NULL 與非 NULL 佔用相同空間 (固定長度)VARCHAR(100):NULL 只佔 NULL 位圖,非 NULL 佔用長度前綴 + 實際數據
CREATE TABLE test_null (
col1 VARCHAR(10) NOT NULL,
col2 VARCHAR(10) NULL
) ENGINE=MyISAM;
-- 測試 1: 插入 NULL 到 NOT NULL 列
INSERT INTO test_null VALUES (NULL, 'abc');
-- ERROR 1048 (23000): Column 'col1' cannot be null
-- 測試 2: 插入空字符串到 NOT NULL 列
INSERT INTO test_null VALUES ('', 'abc');
-- Query OK, 1 row affected
-- 測試 3: 插入 NULL 到 NULL 列
INSERT INTO test_null VALUES ('xyz', NULL);
-- Query OK, 1 row affected
關鍵結論:NOT NULL 只禁止 NULL,不禁止空字符串。應用層需同時驗證業務「必填」邏輯。
INSERT INTO test_null VALUES ('', NULL), ('hello', 'world'), (NULL, '');
-- 查詢 col1 不為空字符串
SELECT * FROM test_null WHERE col1 <> '';
-- 結果: ('hello', 'world') -- 只有 1 行!NULL 和 '' 都被過濾
-- 查詢 col1 不為 NULL
SELECT * FROM test_null WHERE col1 IS NOT NULL;
-- 結果: ('', 'abc'), ('hello', 'world') -- 2 行!空字符串保留
-- 查詢 col1 既非 NULL 也非空字符串
SELECT * FROM test_null WHERE col1 IS NOT NULL AND col1 <> '';
-- 結果: ('hello', 'world') -- 業務通常真正想要的
NOT NULL DEFAULT '' 或 DEFAULT 0IS NULL 查詢的列 → 考慮 DEFAULT 'NULL_STR' + 普通值索引NOT NULLNOT NULL (或配合 ON DELETE SET NULL 需允許 NULL)
-- Java/JDBC 獲取 NULL 安全模式
String name = rs.getString("name"); // 返回 null
if (rs.wasNull()) { /* 處理 NULL */ }
-- MyBatis resultMap 映射
-- Spring Data JPA
@Column(nullable = false) // DDL 生成 NOT NULL
private String name = ""; // Java 層默認空串
-- 將現有 NULL 轉為空字符串 (謹慎執行)
UPDATE users SET nickname = '' WHERE nickname IS NULL;
ALTER TABLE users MODIFY nickname VARCHAR(50) NOT NULL DEFAULT '';
-- 統計 NULL 分布
SELECT
COUNT(*) AS total,
COUNT(nickname) AS not_null_count,
COUNT(*) - COUNT(nickname) AS null_count,
SUM(CASE WHEN nickname = '' THEN 1 ELSE 0 END) AS empty_count
FROM users;
NULL 是關係模型的理論基石,但在工程實踐中往往帶來更多複雜性而非價值。「默認 NOT NULL,業務需要才允許 NULL,應用層統一語義」 是降低認知負擔、提升查詢性能、保證數據質量的鐵律。