Database-Design

設計友誼數據庫結構:我應該使用多值列嗎?

  • December 6, 2020

假設我有一個名為 的表User_FriendList,它具有以下特徵:

CREATE TABLE User_FriendList (
   ID ...,
   User_ID...,
   FriendList_IDs...,
   CONSTRAINT User_Friendlist_PK PRIMARY KEY (ID)
);

讓我們假設該表包含以下數據:

+----+---------+---------------------------+
| **身份證**| **使用者ID** | **好友列表_ID** |
+----+---------+---------------------------+
| 1 | 102 | 2:15:66:35:26:17: |
+----+---------+---------------------------+
| 2 | 114 | 1:12:63:33:24:16:102 |
+----+---------+---------------------------+
| 3 | 117 | 6:24:52:61:23:90:97:118 |
+----+---------+---------------------------+

注意: “:”(冒號)是在PHP中分解為array.

問題

所以:

  • 這是“儲存” a 的便捷方式IDsFriendList
  • 或者,相反,我應該有單獨的行,每行只有一個FriendId值,當我需要檢索給定列表的所有行時,只需執行類似的查詢SELECT * FROM UserFriendList WHERE UserId = 1

管理單個資訊

假設,在您的業務領域,

  • 一個使用者可以有零個或多個好友
  • 朋友必須首先註冊為使用者;和
  • 您將搜尋和/或添加和/或刪除和/或修改朋友列表的單個值;

那麼在多值列中收集的每個特定數據代表一個單獨的資訊,具有非常準確的含義。因此,所述列Friendlist_IDs

  • 需要一組適當的顯式約束,並且
  • 它的值有可能通過幾個關係操作(或它們的組合)單獨操縱。

簡短的回答

因此,您應該保留Friendlist_IDs(a) 列中的每個值,該列僅接受 (b) 表中的每行一個唯一值,該表表示可以在Users之間發生的**概念級別關聯類型,即友誼-如我將在以下部分舉例說明——。

通過這種方式,您將能夠 (i) 將所述表作為數學關係處理,並且 (ii) 將所述列作為數學關係屬性處理——當然,在 MySQL 及其 SQL 方言允許的範圍內——。

為什麼?

因為由E. F. Codd 博士創建的數據關係模型要求擁有由列組成的表,每行僅包含一個適用類型的值;因此,聲明一個包含多個域或類型值的列的表 (1) 並不代表數學關係,並且 (2) 將無法獲得上述理論框架中提出的優勢。

建模使用者之間的友誼:首先定義業務環境規則

我強烈建議開始建構一個數據庫,首先根據相關業務規則的定義來界定相應的概念模式,除其他因素外,這些規則必須描述存在於不同興趣方面之間的相互關係類型,即、適用的實體類型及其屬性;例如:

  • 使用者主要由他或她的UserId**標識
  • 使用者由他或她的FirstNameLastNameGenderBirthdate的組合交替**辨識
  • 使用者由他或她的使用者名交替辨識
  • 使用者是零一或多友誼請求者
  • 使用者是一或多友誼的**收件人
  • 友誼主要由其RequesterIdAddresseeId的組合來辨識

說明性IDEF1X圖

通過這種方式,我能夠推導出圖 1所示的 IDEF1X 1圖,它集成了之前製定的大部分規則:

圖 1. 使用者友誼 IDEF1X 圖

如圖所示,請求者收件人是表示參與給定友誼的特定**使用者所執行的角色的表示。

既然如此,Friendship實體類型描繪了一種多對多(M:N) 基數比的關聯類型,它可能涉及同一實體類型(即User )的**不同出現。因此,它是被稱為“材料清單”或“零件爆炸”的經典結構的一個例子。


1 資訊建模集成定義( IDEF1X ) 是一項高度推薦的技術,於 1993 年 12 月由美國國家標準與技術研究院(NIST)確立為標準。它完全基於 (a) 由關係模型的唯一創始人,即EF Codd 博士撰寫的早期理論材料;關於 (b) 數據的實體關係視圖,由PP Chen 博士開發;以及 (c) Robert G. Brown 創建的邏輯數據庫設計技術。


說明性 SQL-DDL 邏輯設計

然後,從上面顯示的 IDEF1X 圖表中,聲明如下所示的 DDL 安排更加“自然”:

-- You should determine which are the most fitting 
-- data types and sizes for all the table columns 
-- depending on your business context characteristics.

-- At the physical level, you should make accurate tests 
-- to define the mostconvenient INDEX strategies based on 
-- the pertinent query tendencies.

-- As one would expect, you are free to make use of 
-- your preferred (or required) naming conventions. 

CREATE TABLE UserProfile ( -- Represents an independent entity type.
   UserId          INT      NOT NULL,
   FirstName       CHAR(30) NOT NULL,
   LastName        CHAR(30) NOT NULL,
   BirthDate       DATE     NOT NULL,
   GenderCode      CHAR(3)  NOT NULL,
   Username        CHAR(20) NOT NULL,
   CreatedDateTime DATETIME NOT NULL,
   --
   CONSTRAINT UserProfile_PK  PRIMARY KEY (UserId),
   CONSTRAINT UserProfile_AK1 UNIQUE ( -- Composite ALTERNATE KEY.
       FirstName,
       LastName,
       GenderCode,
       BirthDate
   ),
   CONSTRAINT UserProfile_AK2 UNIQUE (Username) -- Single-column ALTERNATE KEY.
);

CREATE TABLE Friendship ( -- Stands for an associative entity type.
   RequesterId     INT      NOT NULL,
   AddresseeId     INT      NOT NULL, -- Fixed with a well-delimited data type.
   CreatedDateTime DATETIME NOT NULL,
   --
   CONSTRAINT Friendship_PK            PRIMARY KEY (RequesterId, AddresseeId), -- Composite PRIMARY KEY.
   CONSTRAINT FriendshipToRequester_FK FOREIGN KEY (RequesterId)
       REFERENCES UserProfile (UserId),
   CONSTRAINT FriendshipToAddressee_FK FOREIGN KEY (AddresseeId)
       REFERENCES UserProfile (UserId)
);

以這種方式:

  • 每個基表代表一個單獨的實體類型;
  • 代表相應實體類型的唯一屬性;
  • 為每一固定一個特定的數據類型a,以保證它包含的所有都屬於一個特定且定義明確的集合,無論是 INT、DATETIME、CHAR 等;和
  • (以聲明方式)配置多個約束b以確保所有中保留的形式的斷言滿足在概念模式中確定的業務規則。

單值列的優點

如圖所示,您可以,例如:

  • 利用數據庫管理系統(為簡潔起見,DBMS)為列強制執行的引用完整性Friendship.AddresseeId,因為將其限制為對列進行引用的 FOREIGN KEY(為簡潔起見,FK)可UserProfile.UserId確保每個值都指向現有行。
  • 創建由列組合組成的複合主鍵(PK)(Friendship.RequesterId, Friendship.AddresseeId),有助於優雅地區分所有插入的行,並自然地​​保護它們的唯一性

當然,這意味著為系統分配的代理值附加一個額外的列(例如,使用Microsoft SQL Server 中的IDENTITY屬性或 MySQL 中的AUTO_INCREMENT屬性設置的列)和輔助 INDEX 是完全多餘的。

  • 將保留的值限制Friendship.AddresseeId為精確的數據類型c(它應該匹配,例如,為 建立的類型UserProfile.UserId,在本例中為 INT),讓 DBMS 負責相關的自動驗證。

這個因素也有助於 (a) 利用相應的內置類型函式和 (b) 優化磁碟空間使用。

  • 通過為列配置小而快速的從屬索引來優化物理級別的數據檢索,因為這些物理元素可以極大地幫助加速涉及所述列的查詢。Friendship.AddresseeId

當然,您可以,例如,為單獨設置一個單列索引Friendship.AddresseeId,一個包含Friendship.RequesterIdand的多列索引Friendship.AddresseeId ,或兩者兼而有之。

  • 避免“搜尋”收集在同一列中的不同值(很可能是重複的、錯誤輸入的等)而引入的不必要的複雜性,這種做法最終會減慢系統的執行速度,因為您會必須求助於資源和耗時的非關係方法來完成所述任務。

因此,有多種原因需要仔細分析相關的業務環境,以便準確地標出每個表列的類型d

如前所述,數據庫設計者所扮演的角色對於充分利用 (1)關係模型提供的邏輯級優勢和 (2)所選 DBMS 提供的物理機制至關重要。


a , b , c , d顯然,在使用支持 DOMAIN 創建(一個獨特的關係特性)的 SQL 平台(例如FirebirdPostgreSQL )時,您可以聲明只接受屬於它們各自的值的列(適當地受約束,有時共享)域。


一個或多個共享正在考慮的數據庫的應用程序

當您必須arrays在應用程序的程式碼中使用訪問數據庫時,您只需完整檢索相關數據集,然後將其“綁定”到相關程式碼結構或執行應該發生的相關應用程序程序。

單值列的更多好處:數據庫結構擴展更容易

將數據點保存在其保留且類型正確的列中的另一個優點AddresseeId是它極大地促進了數據庫結構的擴展,我將在下面舉例說明。

場景進展:納入友誼狀態概念

由於友誼會隨著時間的推移而發展,您可能必須跟踪這種現象,因此您必須 (i) 擴展概念模式並 (ii) 在邏輯佈局中聲明更多表。因此,讓我們安排下一個業務規則來描述新的公司:

  • 一個友誼擁有一對多的友誼狀態
  • FriendshipStatus主要由RequesterIdAddresseeIdSpecifiedDateTime的組合來辨識
  • 使用者指定零一個或多個友誼狀態
  • 一個狀態分類零一個或多個友誼狀態
  • 狀態主要由其StatusCode標識
  • 狀態由其名稱交替標識

擴展 IDEF1X 圖

隨後,可以擴展之前的 IDEF1X 圖,以包括上述新的實體類型和相互關係類型。圖2中顯示了描繪與新元素關聯的先前元素的圖表:

圖 2. 友誼狀態 IDEF1X 圖

邏輯結構添加

之後,我們可以使用以下聲明來延長 DDL 佈局:

--
CREATE TABLE MyStatus ( -- Denotes an independent entity type.
   StatusCode CHAR(1)  NOT NULL,
   Name       CHAR(30) NOT NULL,
   --
   CONSTRAINT MyStatus_PK PRIMARY KEY (StatusCode),
   CONSTRAINT MyStatus_AK UNIQUE      (Name) -- ALTERNATE KEY.
); 

CREATE TABLE FriendshipStatus ( -- Represents an associative entity type.
   RequesterId       INT      NOT NULL,
   AddresseeId       INT      NOT NULL,
   SpecifiedDateTime DATETIME NOT NULL,
   StatusCode        CHAR(1)  NOT NULL,
   SpecifierId       INT      NOT NULL,
   --
   CONSTRAINT FriendshipStatus_PK             PRIMARY KEY (RequesterId, AddresseeId, SpecifiedDateTime), -- Composite PRIMARY KEY.
   CONSTRAINT FriendshipStatusToFriendship_FK FOREIGN KEY (RequesterId, AddresseeId)
       REFERENCES Friendship  (RequesterId, AddresseeId), -- Composite FOREIGN KEY.
   CONSTRAINT FriendshipStatusToMyStatus_FK   FOREIGN KEY (StatusCode)
       REFERENCES MyStatus    (StatusCode),
   CONSTRAINT FriendshipStatusToSpecifier_FK  FOREIGN KEY (SpecifierId)
       REFERENCES UserProfile (UserId)      
);

因此,每次需要更新給定友誼的**狀態時,**使用者只需插入一個新行,其中包含:FriendshipStatus

  • 合適的RequesterIdAddresseeId值——取自相關Friendship行——;
  • 新的和有意義的StatusCode價值——來自MyStatus.StatusCode——;
  • 確切的 INSERTion 時刻,即SpecifiedDateTime——最好使用伺服器功能,以便您可以以可靠的方式檢索和保留它——;和
  • SpecifierId值將指示將UserIdFriendshipStatus輸入系統的相應值(理想情況下,在您的應用程序工具的幫助下)。

就此而言,讓我們假設該MyStatus表包含以下數據 - PK 值是 (a) 最終使用者、應用程序程序員和 DBA 友好的,以及 (b) 在物理實現級別的字節數方面小而快——:

+-——————————-+-—————————-+
| **狀態碼**| **姓名** |
+-——————————-+-—————————-+
| 右 | 要求 |
+------------+-----------+
| 一個 | 接受 |
+------------+-----------+
| D | 拒絕 |
+------------+-----------+
| 乙| 塊狀 |
+------------+-----------+

因此,該FriendshipStatus表可能包含如下所示的數據:

+-———————————-+-———————————-+-———————————————————————-+-——————————-+-———————————-+
| **請求者 ID** | **收件人 ID** | **指定日期時間** | **狀態碼**| **說明符 ID** |
+-———————————-+-———————————-+-———————————————————————-+-——————————-+-———————————-+
| 1750 | 1748 | 2016-04-01 16:58:12.000 | 右 | 1750 |
+-------------+-------------+-------------------------+------------+-------------+
| 1750 | 1748 | 2016-04-02 09:12:05.000 | 一個 | 1748 |
+-------------+-------------+-------------------------+------------+-------------+
| 1750 | 1748 | 2016-04-04 10:57:01.000 | 乙| 1750 |
+-------------+-------------+-------------------------+------------+-------------+
| 1750 | 1748 | 2016-04-07 07:33:08.000 | 右 | 1748 |
+-------------+-------------+-------------------------+------------+-------------+
| 1750 | 1748 | 2016-04-08 12:12:09.000 | 一個 | 1750 |
+-------------+-------------+-------------------------+------------+-------------+

如您所見,可以說該FriendshipStatus表用於組成時間序列


相關文章

您可能還對以下內容感興趣:

  • 在這個答案中,我提出了一種處理兩種不同實體類型之間常見的多對多關係的基本方法。
  • 圖 1 中所示的 IDEF1X 圖說明了另一個答案。請特別注意名為*“婚姻”*和“後代”的實體類型,因為它們是如何處理“零件爆炸問題”的另外兩個範例。
  • 這篇文章簡要討論了在單個列中保存不同的資訊。

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