我正在遵循 Jeff Smith 的“在 SQL Server 中实现表继承”中描述的技术(这似乎是实现这种结构的事实上的方法)。基表与其三个子类型表、、People
具有 1:0..1 关系。传统上,这是通过将子类型表的主键定义为基表的外键来完成的。Students
Teachers
Parents
为了在子类型之间强制执行排他性(防止同一个人同时是学生和教师),作者建议将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)
)
但是,这种方法存在许多问题:
- 它在每个子类型表上浪费了额外的一列空间。
- 它需要对基表附加唯一约束。这会浪费更多空间(因为它将作为唯一索引实现)并减慢对基表的更新。
- 外键约束涉及对两列(而不是一列)的检查,从而减慢了对子类型表的更新。
我的假设是,最好通过标量函数使用检查约束来强制唯一性。这将消除额外列和唯一索引的浪费存储,加快对基表的更新,并有望在更新子类型表时获得与复合外键相同的性能。
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
)
有什么理由不应该使用这种方法吗?