Mariadb

使大於限制的欄位唯一

  • May 14, 2020

首先,只是一個簡短的說明。我一直在尋找答案,但我只能問為什麼而不是如何克服。

我目前正在為我正在從事的項目設計一組數據庫表。幾個表如下(簡化表):

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. 將字元集修改為 1,以更小的佔用空間儲存數據
  2. 減少欄位的長度,使其符合限制
  3. 將電子郵件分成 2 個部分(使用者名、域)並將它們用作唯一值
  4. 利用數據庫檢查程式碼的唯一性
  5. 創建電子郵件欄位的雜湊並將其儲存在另一個欄位中。將此用於唯一約束
  6. 使用插入前和更新前觸發器來強制唯一性

我不太喜歡選項 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 ;

使用這樣的觸發器會對數據庫的性能產生任何負面影響(與上述選項相比)還是我只是想多了?

  1. 實際的電子郵件限制長度可能為 64
  2. 電子郵件地址永遠不是 utf8,只有 ANSI
  3. innodb_large_prefix可以給你一個更大的索引。
  4. 以 HASH 作為唯一索引的生成列同樣好,例如:

ALTER TABLE 成員添加 email_hash VARBINARY(56) AS (SHA2(email, 224)) UNIQUE KEY

引用自:https://dba.stackexchange.com/questions/233624