表 User(phone VARCHAR(20)) 存在數據:
mysql> SELECT phone, COUNT(1) FROM User GROUP BY phone; +-----------------+----------+ | phone | count(1) | +-----------------+----------+ | NULL | 70 | | | 40 | <-- 空字符串 '' | +86-13390889711 | 1 | | +86-13405053385 | 1 | +-----------------+----------+
執行 ALTER TABLE User ADD CONSTRAINT uk_phone UNIQUE(phone); 報錯:
ERROR 1062 (23000): Duplicate entry '' for key 'uk_phone'
NULL 代表「未知/無值」,多個 NULL **不視為重複**,唯一索引允許任意數量 NULL。phone = '' 觸發重複鍵錯誤。-- 1. 確認空字符串數量 SELECT COUNT(1) FROM User WHERE phone = ''; -- 2. 將空字符串統一轉為 NULL (業務語義一致) UPDATE User SET phone = NULL WHERE phone = ''; -- Query OK, 40 rows affected -- 3. 再次創建唯一索引 ALTER TABLE User ADD CONSTRAINT uk_phone UNIQUE(phone); -- Query OK, 0 rows affected
CREATE TABLE User (
phone VARCHAR(20) DEFAULT NULL, -- 顯式 DEFAULT NULL
-- 或添加 CHECK (MySQL 8.0.16+)
CONSTRAINT chk_phone CHECK (phone IS NULL OR phone <> '')
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
// Java 示例
String phone = StringUtils.trimToNull(request.getParameter("phone"));
// 前端傳 "" → null,傳 " " → null,傳 "138" → "138"
| 場景 | 允許重複 NULL | 說明 |
|---|---|---|
| 單列唯一索引 | ✅ 是 | 標準 SQL 行為 |
| 組合唯一索引 (a,b) | ✅ 是 | 任意列為 NULL 即視為不同組合 |
| 主鍵 (PK) | ❌ 否 | PK 列隱含 NOT NULL |
-- 檢查表中是否存在空字符串將導致唯一索引失敗的列
SELECT
TABLE_NAME,
COLUMN_NAME,
SUM(CASE WHEN COLUMN_NAME = '' THEN 1 ELSE 0 END) AS empty_count,
SUM(CASE WHEN COLUMN_NAME IS NULL THEN 1 ELSE 0 END) AS null_count
FROM INFORMATION_SCHEMA.COLUMNS
JOIN 相應表 -- 需動態構建
WHERE DATA_TYPE IN ('char','varchar','text')
GROUP BY TABLE_NAME, COLUMN_NAME
HAVING empty_count > 1;
理解 NULL 與空字符串的語義邊界,在建模、開發、遷移三階段統一規範,徹底消除唯一索引創建失敗這類低級錯誤。