1

我正在遵循 Jeff Smith 的“在 SQL Server 中实现表继承”中描述的技术(这似乎是实现这种结构的事实上的方法)。基表与其三个子类型表、、People具有 1:0..1 关系。传统上,这是通过将子类型表的主键定义为基表的外键来完成的。StudentsTeachersParents

为了在子类型之间强制执行排他性(防止同一个人同时是学生和教师),作者建议将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
) 

有什么理由不应该使用这种方法吗?

4

1 回答 1

1

额外的存储空间实际上是最小的,如果您的人员类型列是一个 tinyint(最多 255 种人员),那么您仍然每人只使用一个额外的字节。所以这不应该是决定的一个重要因素,主要问题是标量 udf 的性能明显低于外键约束。这已经过测试,文章标量 UDF 包装在 CHECK 约束中显示的结果非常慢,并且可能会因多行更新而失败

测试也包含在这个 SO 答案中

于 2014-06-03T22:02:05.673 回答