Mariadb
使大於限制的欄位唯一
首先,只是一個簡短的說明。我一直在尋找答案,但我只能問為什麼而不是如何克服。
我目前正在為我正在從事的項目設計一組數據庫表。幾個表如下(簡化表):
CREATE TABLE members ( id int(11) NOT NULL AUTO_INCREMENT email varchar(255) NOT NULL PRIMARY KEY (id) ) ENGINE = INNODB, CHARACTER SET utf8mb4, COLLATE utf8mb4_general_ci;
我想確保電子郵件保持唯一,但是由於欄位的長度以及我使用 utf8mb4 字元集的事實,我無法使用唯一約束(嘗試這樣做會導致錯誤 sch 為“指定鍵太長了;最大密鑰長度為 767 字節”)。
為了克服這個問題,我似乎有以下選擇:
- 將字元集修改為 1,以更小的佔用空間儲存數據
- 減少欄位的長度,使其符合限制
- 將電子郵件分成 2 個部分(使用者名、域)並將它們用作唯一值
- 利用數據庫檢查程式碼的唯一性
- 創建電子郵件欄位的雜湊並將其儲存在另一個欄位中。將此用於唯一約束
- 使用插入前和更新前觸發器來強制唯一性
我不太喜歡選項 1(或者我只是在這種情況下使用了錯誤的字元集?)、2(即使這個欄位實際上對於根據 RFC 的有效電子郵件地址來說太短)和 3(域如果遵循 RFC 的規則仍然會太長),因為我不認為數據庫的結構應該被妥協以滿足簡單的約束。
4 依靠程式碼來確保數據庫符合要求。我對此並不太熱衷,因為如果有人忘記檢查,它可能會導致儲存的數據失去完整性。
5 將需要將無關數據儲存在數據庫中。這可能會導致衝突,但取決於散列算法,這可能不是問題。
6 將實施如下:
DELIMITER $$ CREATE DEFINER = 'user'@'localhost' TRIGGER MemberBeforeInsertTrigger BEFORE INSERT ON members FOR EACH ROW BEGIN DECLARE userCount int; SELECT COUNT(email) INTO userCount FROM members WHERE email = new.email; -- Ensure Email is not in use. IF userCount > 0 THEN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Email address already in use'; END IF; END $$ DELIMITER ;
使用這樣的觸發器會對數據庫的性能產生任何負面影響(與上述選項相比)還是我只是想多了?
- 實際的電子郵件限制長度可能為 64
- 電子郵件地址永遠不是 utf8,只有 ANSI
- innodb_large_prefix可以給你一個更大的索引。
- 以 HASH 作為唯一索引的生成列同樣好,例如:
ALTER TABLE 成員添加 email_hash VARBINARY(56) AS (SHA2(email, 224)) UNIQUE KEY