基本 RDBMS 表關係
如果您有一個“主表”和幾個“子表”的設計,最好是主表“連結”到每個子表,子錶鍊接到主表,還是建立雙向連結? ? 子表中存在的數據可能不存在(即它可能與特定條目無關,因此不會創建子表中的連結條目)。基本上數據將以一對(零或一)的方式相關。永遠不會有理由在不涉及主表的情況下在子表之間進行連結。
具體來說,我更關心 MS SQL Server (2008R2+)
**範例設計 1:**主錶鍊接到子表
CREATE TABLE tMaster ( PK_TM int IDENTITY(1,1) NOT NULL PRIMARY KEY CLUSTERED ,IntrinsicField1 datetime NOT NULL ,IntrinsicField2 int NOT NULL ,IntrinsicField3 nvarchar(255) NULL ,index_subA int NULL --index created on this value. links to PK_SA of appropriate entry if it exists ,index_subB int NULL --index created on this value .... ,index_subM int NULL --index created on this value ) CREATE TABLE tSubA PK_SA int IDENTITY(1,1) NOT NULL PRIMARY KEY CLUSTERED ,ExtraField1 int NOT NULL ,ExtraField2 varchar(24) NOT NULL ) CREATE TABLE tSubB PK_SB int IDENTITY(1,1) NOT NULL PRIMARY KEY CLUSTERED ,ExtF1 NUMERIC(5,3) NOT NULL ,ExtF2 NUMERIC(5,3) NOT NULL ) CREATE TABLE tSubM PK_SM int IDENTITY(1,1) NOT NULL PRIMARY KEY CLUSTERED ,EField1 int NOT NULL ,EField2 nvarchar(100) NOT NULL )
**範例設計 2:**子錶鍊接到主表
CREATE TABLE tMaster ( PK_TM int IDENTITY(1,1) NOT NULL PRIMARY KEY CLUSTERED ,IntrinsicField1 datetime NOT NULL ,IntrinsicField2 int NOT NULL ,IntrinsicField3 nvarchar(255) NULL ) CREATE TABLE tSubA lMaster_ID int NOT NULL PRIMARY KEY CLUSTERED -- value of PK_TM of cross-linked entry ,ExtraField1 int NOT NULL ,ExtraField2 varchar(24) NOT NULL ) CREATE TABLE tSubB lMaster_ID int NOT NULL PRIMARY KEY CLUSTERED ,ExtF1 NUMERIC(5,3) NOT NULL ,ExtF2 NUMERIC(5,3) NOT NULL ,Master_ID int NOT NULL --index created on this value. links to PK_TM of appropriate entry ) CREATE TABLE tSubM lMaster_ID int NOT NULL PRIMARY KEY CLUSTERED ,EField1 int NOT NULL ,EField2 nvarchar(100) NOT NULL )
**範例設計 3:**雙向連結
CREATE TABLE tMaster ( PK_TM int IDENTITY(1,1) NOT NULL PRIMARY KEY CLUSTERED ,IntrinsicField1 datetime NOT NULL ,IntrinsicField2 int NOT NULL ,IntrinsicField3 nvarchar(255) NULL ,index_subA int NULL --index created on this value. links to PK_SA of appropriate entry if it exists ,index_subB int NULL --index created on this value .... ,index_subM int NULL --index created on this value ) CREATE TABLE tSubA PK_SA int IDENTITY(1,1) NOT NULL PRIMARY KEY CLUSTERED ,ExtraField1 int NOT NULL ,ExtraField2 varchar(24) NOT NULL ,lMaster_ID int NOT NULL ) CREATE TABLE tSubB PK_SB int IDENTITY(1,1) NOT NULL PRIMARY KEY CLUSTERED ,ExtF1 NUMERIC(5,3) NOT NULL ,ExtF2 NUMERIC(5,3) NOT NULL ,lMaster_ID int NOT NULL ) CREATE TABLE tSubM PK_SM int IDENTITY(1,1) NOT NULL PRIMARY KEY CLUSTERED ,EField1 int NOT NULL ,EField2 nvarchar(100) NOT NULL ,lMaster_ID int NOT NULL )
*更好的可能是
- 快點
- 隨著數據庫條目的添加和/或偶爾刪除,不太可能隨著時間的推移而崩潰
- 其他 DBA 更容易快速理解
- 對於普通最終使用者來說更直覺
- 通過添加額外的子表類型更容易擴展
- 數據更緊湊
- 更容易修改為子表的多個副本的一個主控
- 更容易修改為子表的單個副本的多個主控
- 在此處插入您自己的定義
(這裡較少強調選項 7 和 8)
這裡真的沒什麼好說的。至少,傳統智慧是非常清楚的。只有選項 2 應該被考慮,除非你有一個非常非常好的理由,基於你的數據庫的實際經驗,而不是這樣做。
選項 1 和選項 3 都沒有**第一範式 (1NF)**的父表,因為它們包含重複列,即子表的外鍵。
在事務系統中,您應該從假設您的表至少應該在 3NF 中開始。僅出於一個很好的,經過深思熟慮的理由而遠離它。
一個這樣的原因可能是有很多子表,而您不知道可能需要將哪些子表連接到父表以獲取特定的父記錄。取決於許多因素,可能會導致性能問題。如果確實發生了此問題,您可以使用的一種折衷方案(您可能會考慮選項 4)是在父表上設置位標誌,指示每種類型的子表的存在。這仍然在乞求最終的數據不一致問題,但它至少不會比選項 3 更不一致,因為它可能只會將您指向一個空連接,而不是指向一個不正確的記錄。
我個人更喜歡選項 2,而且我過去曾多次使用過這種設計——尤其是當我嘗試對類似於程式碼中繼承方式的層次結構進行建模時。
我更喜歡選項 2 的原因是因為它使主表保持清潔。在物件導向程式中實現繼承時,基類應該對派生自它的類一無所知。知道了這一點,我們可以立即打折選項 1 和 3,因為您需要為每種可能的子類類型添加引用。根據需要更改的頻率,這將使維護變得非常痛苦。使用選項 2 使得添加更多子類變得非常簡單,因為您不太可能破壞現有查詢。
此外,我會打折選項 3,因為您必須儲存鍵的次數超過必要的次數,並且如果您要
FOREIGN KEY
對這些列設置約束(如您所願) - 數據庫引擎會阻止您這樣做,因為它會導致循環引用.此外,如果您將
FOREIGN KEY
約束設置為ON DELETE CASCADE
它,將確保沒有它的父記錄就不會存在子類記錄。我通常不使用此功能並且一般建議不要使用它,但這是一個很好的案例,它確實對您有用,並且使死記錄的維護變得更加簡單。如果您將所有“共享”屬性保留為主表上的列,並且僅包含子類表中特定於子類的屬性,那麼您將最小化所需的儲存空間。
在速度方面,不知道硬體和數據大小/行數是主觀的 - 但是加入單個表(子類到主表)將使您的程式碼比使用更複雜的範例,例如選項 3。