Mysql

如何避免 3 個表之間的循環依賴(循環引用)?

  • November 17, 2021

我有 3 張桌子:

  • 人們
  • 郵政
  • 喜歡

當我設計 ER 模型時,它具有循環依賴:

1:N
人 --------< 發表

1:N
發表 ----------< 贊

1:N
人 --------< 喜歡

邏輯是:

  • 1個人可以有很多文章。
  • 1 個文章有很多贊。
  • 1 人可以點贊很多文章(創建的人不能點贊自己的文章)。

我怎樣才能消除這種循環設計?還是我的數據庫設計錯誤?

商業規則

讓我們對您提出的業務規則進行一些改寫:

  • APerson創建零一或多 Posts
  • APost接收零一或多 Likes
  • APerson表現出零一或多 Likes每一個都一個特定 Post的 相關。

邏輯模型

然後,從這樣一組斷言中,我導出了兩個邏輯級別**IDEF1X$$ 1 $$**數據模型如圖 1所示。

圖 1 - 人員和文章數據模型

選項 A

正如您在選項 A 模型中看到的那樣,PersonId **遷移$$ 2 $$from PersontoPost作為外鍵 (FK),但它接收角色名稱$$ 3 $$**的AuthorId,並且該屬性與 一起構成實體類型PostNumber的主鍵(PK) 。Post

我假設 aLike只能與特定的 關聯存在Post,因此我設置了一個Like包含三個不同屬性的 PK PostAuthorIdPostNumberLikerIdPostAuthorId和的組合PostNumber是正確引用PostPK 的 FK。LikerId反過來,它是一個與 建立適當關聯的 FK Person.PersonId

借助此結構,您可以確保一個確定的人只能Like對同一Post實例顯示一次事件。

防止文章作者喜歡他自己的文章的方法

由於您希望一個人喜歡他/她撰寫的文章,因此一旦在實施階段,您應該建立一個方法,將 的值Like.PostAuthorIdLike.LikerId每次 INSERT 嘗試中的值進行比較。如果所述值匹配,**(a)您拒絕插入,如果它們不匹配(b)**您讓該過程繼續。

為了在您的數據庫中完成此任務,您可以使用:

  1. 一個CHECK CONSTRAINT但是,當然,這個方法不包括 MySQL,因為到目前為止它還沒有在這個平台上實現,你可以在這里這裡看到。
  2. ACID Transaction中的程式碼行
  3. TRIGGER中的程式碼行,可以返回指示違反規則嘗試的自定義消息。

選項 B

如果作者不是以主要方式辨識您的業務領域中的文章的屬性,您可以使用類似於選項 B 中描述的結構。

這種方法還確保文章只能被同一個人點贊一次。


筆記

  1. 資訊建模集成定義 ( IDEF1X ) 是一種高度推薦的數據建模技術,於1993 年 12 月被美國國家標準與技術研究院 ( NIST )定義為標準

  2. IDEF1X 將鍵遷移定義為“將父實體或通用實體的主鍵作為外鍵放置在其子實體或類別實體中的建模過程”。

3.角色名稱是分配給外鍵屬性的一種表示,以便在其相應實體類型的上下文中表達該屬性的含義。自 1970 年以來, EF Codd 博士在其題為“大型共享數據庫的數據關係模型”的開創性論文中推薦了角色命名。就其本身而言,IDEF1X——保持對關係實踐的忠誠——也提倡這一程序。

我在這裡看不到任何循環。這些實體之間存在人員職位以及兩種獨立的關係。我認為喜歡是其中一種關係的實現。

  • 一個人可以寫很多文章,一個文章是一個人寫的:1:n
  • 一個人可以喜歡很多文章,一個文章可以被很多人喜歡:n:m

n:m 關係可以用另一個關係來實現:likes

基本實現

PostgreSQL中的基本實現可能如下所示:

CREATE TABLE person (
 person_id serial PRIMARY KEY
, person    text NOT NULL
);

CREATE TABLE post (
 post_id   serial PRIMARY KEY
, author_id int NOT NULL  -- cannot be anonymous
    REFERENCES person ON UPDATE CASCADE ON DELETE CASCADE  -- 1:n relationship
, post      text NOT NULL
);

CREATE TABLE likes (  -- n:m relationship
 person_id int REFERENCES person ON UPDATE CASCADE ON DELETE CASCADE
, post_id   int REFERENCES post ON UPDATE CASCADE ON DELETE CASCADE
, PRIMARY KEY (post_id, person_id)
);

文章必須有作者(NOT NULL),而任何喜歡的存在都是可選的。但是,對於現有的喜歡,post並且person 必須都被引用 - 由隱式PRIMARY KEY生成兩個列的 強制執行。NOT NULL所以匿名點贊是​​不可能的。

關於 n:m 實現:

防止自戀

你寫了:

(創建的人不能喜歡他自己的文章)。

在上面的實現中還沒有強制執行。您可以使用觸發器

或者這些更快/更可靠的解決方案之一:

堅如磐石的成本

如果它需要堅如磐石,請將 FK 從 擴展likespost以包含author_id冗餘。CHECK然後你可以用一個簡單的約束來排除亂倫:

CREATE TABLE likes (
 person_id int REFERENCES person ON UPDATE CASCADE ON DELETE CASCADE
, post_id   int 
, **author_id int NOT NULL**
, CONSTRAINT likes_pkey PRIMARY KEY (post_id, person_id)
, **CONSTRAINT likes_post_fkey FOREIGN KEY (author_id, post_id)
REFERENCES post(author_id, post_id) ON UPDATE CASCADE ON DELETE CASCADE
, CONSTRAINT no_self_like CHECK (person_id <> author_id)**
);

需要在 中的其他冗餘UNIQUE約束post

ALTER TABLE post ADD CONSTRAINT post_for_fk_uni UNIQUE (author_id, post_id);

author_id首先提供一個有用的索引,同時在它上面。

有關的:

CHECK約束條件更便宜

建立在上面的*“基本實現”*之上。

CHECK約束是不可變的。引用另一個表從來都不是一成不變的,我們在這裡有點濫用這個概念。聲明約束**NOT VALID**以正確反映這一點。看:

CHECK這種特殊情況下,約束似乎是合理的,因為文章的作者似乎是一個永遠不會改變的屬性。確保不允許更新該欄位。

我們偽造一個IMMUTABLE函式:

CREATE OR REPLACE FUNCTION f_author_id_of_post(_post_id int)
 RETURNS int
 LANGUAGE sql IMMUTABLE AS
'SELECT p.author_id FROM public.post p WHERE p.post_id = $1';

將“public”替換為表的實際架構。在約束

中使用此函式:CHECK

ALTER TABLE likes ADD CONSTRAINT no_self_like_chk
CHECK (f_author_id_of_post(post_id) <> person_id) NOT VALID;

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