Sql-Server

基本 RDBMS 表關係

  • April 23, 2017

如果您有一個“主表”和幾個“子表”的設計,最好是主表“連結”到每個子表,子錶鍊接到主表,還是建立雙向連結? ? 子表中存在的數據可能不存在(即它可能與特定條目無關,因此不會創建子表中的連結條目)。基本上數據將以一對(零或一)的方式相關。永遠不會有理由在不涉及主表的情況下在子表之間進行連結。

具體來說,我更關心 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
)

*更好的可能是

  1. 快點
  2. 隨著數據庫條目的添加和/或偶爾刪除,不太可能隨著時間的推移而崩潰
  3. 其他 DBA 更容易快速理解
  4. 對於普通最終使用者來說更直覺
  5. 通過添加額外的子表類型更容易擴展
  6. 數據更緊湊
  7. 更容易修改為子表的多個副本的一個主控
  8. 更容易修改為子表的單個副本的多個主控
  9. 在此處插入您自己的定義

(這裡較少強調選項 7 和 8)

這裡真的沒什麼好說的。至少,傳統智慧是非常清楚的。只有選項 2 應該被考慮,除非你有一個非常非常好的理由,基於你的數據庫的實際經驗,而不是這樣做。

選項 1 和選項 3 都沒有**第一範式 (1NF)**的父表,因為它們包含重複列,即子表的外鍵。

在事務系統中,您應該從假設您的表至少應該在 3NF 中開始。僅出於一個很好的,經過深思熟慮的理由而遠離它。

一個這樣的原因可能是有很多子表,而您不知道可能需要將哪些子表連接到父表以獲取特定的父記錄。取決於許多因素,可能會導致性能問題。如果確實發生了此問題,您可以使用的一種折衷方案(您可能會考慮選項 4)是在父表上設置位標誌,指示每種類型的子表的存在。這仍然在乞求最終的數據不一致問題,但它至少不會比選項 3 更不一致,因為它可能只會將您指向一個空連接,而不是指向一個不正確的記錄。

我個人更喜歡選項 2,而且我過去曾多次使用過這種設計——尤其是當我嘗試對類似於程式碼中繼承方式的層次結構進行建模時。

我更喜歡選項 2 的原因是因為它使主表保持清潔。在物件導向程式中實現繼承時,基類應該對派生自它的類一無所知。知道了這一點,我們可以立即打折選項 1 和 3,因為您需要為每種可能的子類類型添加引用。根據需要更改的頻率,這將使維護變得非常痛苦。使用選項 2 使得添加更多子類變得非常簡單,因為您不太可能破壞現有查詢。

此外,我會打折選項 3,因為您必須儲存鍵的次數超過必要的次數,並且如果您要FOREIGN KEY對這些列設置約束(如您所願) - 數據庫引擎會阻止您這樣做,因為它會導致循環引用.

此外,如果您將FOREIGN KEY約束設置為ON DELETE CASCADE它,將確保沒有它的父記錄就不會存在子類記錄。我通常不使用此功能並且一般建議不要使用它,但這是一個很好的案例,它確實對您有用,並且使死記錄的維護變得更加簡單。

如果您將所有“共享”屬性保留為主表上的列,並且僅包含子類表中特定於子類的屬性,那麼您將最小化所需的儲存空間。

在速度方面,不知道硬體和數據大小/行數是主觀的 - 但是加入單個表(子類到主表)將使您的程式碼比使用更複雜的範例,例如選項 3。

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