簡體   English   中英

數據庫設計 - 一對多關系中的“特殊”條目

[英]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'數據類型。

下一步是將此值限制為1NULL ,不允許任何其他值。 不幸的是,MySQL不強制執行CHECK約束,因此如果我們希望數據庫強制執行此規則,我們需要實現BEFORE INSERT/BEFORE UPDATE觸發器來強制執行它。

最后,我們添加一個UNIQUE約束

... ON homies (bro_id, main_man)

有了它,MySQL將只允許每個bro_id的main_man值為1bro_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 NULLMAIN_MAN <=> NULLORDER BY main_man, ... ,如果要在客戶端上對其進行排序,則返回SELECTmain_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是前導列。 這就是我們在添加外鍵約束之前添加索引的原因。

接下來是一種非常標准的一對多關系建模方法,其中一個子行被認為是“特殊的”:

在此輸入圖像描述

該模型具有以下重要屬性:

  • 除了從孩子到父母的正常FK之外,我們還使用從父母到孩子的“反向”FK。
  • 我們使用識別關系 ,使孩子成為弱實體 (即孩子的鑰匙包含從父母遷移的鑰匙)。 兄弟由他所屬的同性戀者以及他在該特定同性戀者中的“數字”(BRO_NO)來識別。 不同homie中的不同兄弟可以具有相同的BRO_NO。

總之,這兩個屬性確保:

  • 每個父母至多有一個孩子是特殊的。 1
  • 父級不能在其自己的子集外部選擇特殊行 - 請注意HOMIE表中的FK1不僅僅是MAIN_MAN_ID,還包括HOMIE_ID。

但是,在並發環境中 ,您必須小心如何生成BRO_NO。 一些可能性:

  • 使其自動遞增並在值中使用“孔”。
  • 鎖定父級然后使用MAX + 1。
  • 只需使用MAX + 1而不進行鎖定,但要准備好處理密鑰沖突並在並發事務嘗試插入相同值時重試INSERT。

如果有其他表引用BRO,您可以考慮添加代理鍵(例如BRO_ID)。 有關代理鍵的利弊,請參閱此處

順便說一句,上面的模型有一個變化:失去反向FK並且只考慮具有最小 BRO_NO的兄弟。 如果您事先知道特殊兄弟,或者您不介意更新密鑰(並且可能級聯更改)以將兄弟移動到頂部,那么這很好。


1如果一個DBMS支持延遲的限制,FK父可以由非NULL,並確保只有一個孩子是特殊的(不只是零或一)。 當存在圓形FK時插入新數據時,其中一個FK會破壞雞和蛋的問題。 不幸的是,MySQL不支持延遲約束。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM