簡體   English   中英

表繼承中的排他性:復合外鍵與檢查約束

[英]Enforcing exclusivity in table inheritance: composite foreign key vs check constraint

我正在遵循Jeff Smith的“ 在SQL Server中實現表繼承 ”中描述的技術(這似乎是實現這種結構的事實上的方法)。 People基表與它的三個亞型表StudentsTeachersParents有1:0..1關系。 傳統上,這是通過將子類型表的主鍵定義為基表的外鍵來完成的。

為了加強子類型之間的排他性(防止同一個人同時成為學生和老師),作者建議將PersonTypeID作為持久化的計算列添加到每個子類型表中,並將其包括在基表的外鍵約束中。

CREATE TABLE PersonType 
(
    PersonTypeID INT PRIMARY KEY,
    Description  VARCHAR(10)
);

INSERT INTO PersonType 
VALUES (1, 'Student'),
       (2, 'Teacher'),
       (3, 'Parent');

CREATE TABLE People 
( 
    PersonID     INT PRIMARY KEY,
    PersonTypeID INT REFERENCES PersonType (PersonTypeID), 
    Name         VARCHAR(10),
    UNIQUE (PersonID, PersonTypeID)
)

CREATE TABLE Students 
( 
    PersonID INT PRIMARY KEY, 
    PersonTypeID AS 1 PERSISTED, -- student 
    EnrollmentDate DATETIME, 
    FOREIGN KEY (PersonID, PersonTypeID) REFERENCES People (PersonID, PersonTypeID) 
) 

CREATE TABLE Teachers 
( 
    PersonID INT PRIMARY KEY, 
    PersonTypeID AS 2 PERSISTED, -- teacher 
    HireDate DATETIME, 
    FOREIGN KEY (PersonID, PersonTypeID) REFERENCES People (PersonID, PersonTypeID) 
) 

CREATE TABLE Parents 
( 
    PersonID INT PRIMARY KEY, 
    PersonTypeID AS 3 PERSISTED, -- parents 
    DifficultyScore INT, 
    FOREIGN KEY (PersonID, PersonTypeID) REFERENCES People (PersonID, PersonTypeID) 
) 

但是,這種方法存在許多問題:

  1. 這浪費了每個子類型表的額外一列空間。
  2. 它在基表上需要附加的唯一約束。 這浪費了更多空間(因為它將被實現為唯一索引),並且減慢了對基表的更新。
  3. 外鍵約束涉及檢查兩列(而不是一列),從而減慢了對子類型表的更新。

我的假設是,最好通過標量函數使用檢查約束來強制執行唯一性。 這樣可以避免多余的列和唯一索引浪費存儲空間,加快對基表的更新,並希望實現子類型表更新的性能與復合外鍵相同。

CREATE TABLE People 
( 
    PersonID     INT PRIMARY KEY,
    PersonTypeID INT REFERENCES PersonType (PersonTypeID), 
    Name         VARCHAR(10)
)

CREATE FUNCTION GetPersonTypeID (@PersonID INT)
RETURNS INT
AS
BEGIN
    RETURN 
    (
        SELECT PersonTypeID 
        FROM People
        WHERE PersonID = @PersonID
    )
END;

CREATE TABLE Students 
( 
    PersonID INT PRIMARY KEY REFERENCES People (PersonID) 
        CHECK (dbo.GetPersonTypeID(PersonID) = 1),
    EnrollmentDate DATETIME
) 

CREATE TABLE Teachers 
( 
    PersonID INT PRIMARY KEY REFERENCES People (PersonID)
        CHECK (dbo.GetPersonTypeID(PersonID) = 2),
    HireDate DATETIME
) 

CREATE TABLE Parents 
( 
    PersonID INT PRIMARY KEY REFERENCES People (PersonID)
        CHECK (dbo.GetPersonTypeID(PersonID) = 3),
    DifficultyScore INT
) 

有什么理由不應該使用這種方法?

實際上,額外的存儲空間很小,如果您的人員類型列是tinyint(最多255種人員),那么您仍然每人僅使用一個字節的額外空間。 因此,這不應成為決定的主要因素,主要問題是標量udf的性能明顯比外鍵約束差。 已對此進行了測試,並且用CHECK約束包裝的標量UDF中顯示的結果非常慢,並且可能無法進行多行更新

測試也包含在該SO答案中

暫無
暫無

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

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