[英]Database Design - “special” entry in one to many relationship
假設我在一對多關系中有兩個表。 我們將第一個稱為Bros,第二個稱為Homies。 兄弟可以有多個兄弟,但其中只有一個可以成為他的“主要人物”。
(看,例子很難。不要對我大喊大叫。)
我怎么代表那個? 我可以在bros表中放入一個'main_man'條目,但這會重復我在homies表中的條目。
我也可以在同性戀表中加入一個條目,但這不會限制其他同性戀者成為主要人物。
有沒有正確的方法來做到這一點? 是否更容易以錯誤的方式執行並使用應用程序來處理它?
有幾種選擇可以對此進行建模。
首先,沒有一個main_man
也必須是一個homie
? 如果是這樣,我會在homies
表上添加一個標志。 MySQL數據類型有點不完美,但我使用的是布爾值,我們總是映射到TINYINT(1) DEFAULT NULL COMMENT 'boolean'
數據類型。
下一步是將此值限制為1
或NULL
,不允許任何其他值。 不幸的是,MySQL不強制執行CHECK約束,因此如果我們希望數據庫強制執行此規則,我們需要實現BEFORE INSERT/BEFORE UPDATE
觸發器來強制執行它。
最后,我們添加一個UNIQUE
約束
... ON homies (bro_id, main_man)
有了它,MySQL將只允許每個bro_id
的main_man值為1
的bro_id
。
這與NULL的標准模式略有不同,意思是“未知”,我認為這是由Microsoft文檔所支持的。 在我們的實現中,我們使用NULL值來表示“不,不是main_man”。 允許NULL
值的主要優點是SQL(通常)和MySQL特別不認為NULL值是另一個NULL值的“重復”。 UNIQUE約束允許多行具有NULL值。 (我認為有一些SQL_MODE設置會改變這種行為,但我們不會去那里。)
為了得到公正的homies
是一個main_man
...
WHERE main_man = 1
或者,更簡潔地說,因為我們沒有使用零來表示TRUE,並且如果我們確定不存在其他非零值...
WHERE main_man
另一個邏輯非常簡單,檢查main_man IS NULL
或MAIN_MAN <=> NULL
, ORDER BY main_man, ...
,如果要在客戶端上對其進行排序,則返回SELECT
的main_man
列。
您可以考慮使用MySQL ENUM
數據類型,只要我們允許NULL值,並且我們驗證MySQL將允許並強制ENUM列上的UNIQUE約束。 (我以前從未嘗試過)。
這只是幾種方法中的一種,但它是我過去成功使用的方法之一。
-
示范
CREATE TABLE bro
( id INT UNSIGNED NOT NULL PRIMARY KEY
) ENGINE=INNODB;
CREATE TABLE homie
( id INT UNSIGNED NOT NULL PRIMARY KEY
, bro_id INT UNSIGNED NOT NULL COMMENT 'FK ref bros.id'
, main_man TINYINT(1) DEFAULT NULL COMMENT 'boolean, 1=is the main man'
, homie_name VARCHAR(10)
) ENGINE=INNODB;
ALTER TABLE homie
ADD UNIQUE INDEX homie_UX1 (bro_id, main_man);
ALTER TABLE homie
ADD CONSTRAINT FK_homie_bro FOREIGN KEY (bro_id) REFERENCES bro (id);
TODO:添加BEFORE INSERT / BEFORE UPDATE觸發器以限制main_man
列的值。
通過添加一些行測試,並檢查我們不能有一個以上的main_man
對於給定bro_id
。
INSERT INTO bro (id) VALUES
(2),(3);
INSERT INTO homie (id, bro_id, main_man, homie_name) VALUES
( 11, 2, NULL, 'mr.slate' )
, ( 12, 2, 1, 'barney')
;
-- attempt to insert another main_man
INSERT INTO homie (id, bro_id, main_man, homie_name) VALUES
( 13, 2, 1, 'wilma' )
;
-- Error Code: 1062
-- Duplicate entry '2-1' for key 'homie_UX1'
UPDATE homie SET main_man = 1 WHERE id = 11 ;
-- Error Code: 1062
-- Duplicate entry '2-1' for key 'homie_UX1'
注意:我忽略了,作為一個小小的獎勵, homie_UX1
索引(為強制執行UNIQUE
約束而創建)也用於支持外鍵,因為bro_id是前導列。 這就是我們在添加外鍵約束之前添加索引的原因。
接下來是一種非常標准的一對多關系建模方法,其中一個子行被認為是“特殊的”:
該模型具有以下重要屬性:
總之,這兩個屬性確保:
但是,在並發環境中 ,您必須小心如何生成BRO_NO。 一些可能性:
如果有其他表引用BRO,您可以考慮添加代理鍵(例如BRO_ID)。 有關代理鍵的利弊,請參閱此處 。
順便說一句,上面的模型有一個變化:失去反向FK並且只考慮具有最小 BRO_NO的兄弟。 如果您事先知道特殊兄弟,或者您不介意更新密鑰(並且可能級聯更改)以將兄弟移動到頂部,那么這很好。
1如果一個DBMS支持延遲的限制,FK父可以由非NULL,並確保只有一個孩子是特殊的(不只是零或一)。 當存在圓形FK時插入新數據時,其中一個FK會破壞雞和蛋的問題。 不幸的是,MySQL不支持延遲約束。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.