您有两个状态值,已批准和已拒绝。已批准进一步细分为两个子状态值,已交付和已取消。让我们首先说我们有三个状态值,Rejected、Delivered 和 Canceled,然后看看我们的设计是如何实现的。
WorkOrder 表将如问题所示,因此我们需要一个 Status 表,它将状态与工单相关联,包含所有状态值共有的任何数据,并提示我们数据的位置可以找到状态。
create table Status(
WOID int primary key,
Value char( 1 ) check( Value in ('R', 'D', 'C' )),
By int not null, -- who approved, rejected or authorized this status
AsOf date not null, -- and when.
constraint FK_Status_WorkOrder( WOID)
references WordOrder( ID ),
constraint FK_By_Employee( By )
references Employee( ID )
);
此时,我们知道将有多个表用于包含状态详细信息(命名为猜测,例如 Rejects、Deliveries、Cancels)。但是我们不能定义一个字段作为其他表的 FK,并且定义三个这样的字段会使运行数据库的设计和数据完整性维护变得复杂。幸运的是,有办法解决它。
我们所做的是让三个详细表中的 FK 引用状态表。但首先,我们必须通过添加一个额外的约束来准备状态表:
create table Status(
...,
constraint UQ_Status_By_Type unique( WOID, Value )
);
如果您注意了,您可能想知道为什么我们要在两个字段上创建唯一索引,其中一个字段本身就是唯一的。原因是定义了三个明细表可以锁定的钩子。
create table Rejects(
WOID int primary key,
Value char( 1 ) check( Value = 'R' ),
Reason varchar( 10 ),
constraint FK_Rejects_Status( WOID, Value )
references Status( WOID, Value )
);
其他明细表的定义类似。
让我们检查一下我们目前所拥有的。状态表的 WOID 字段确保任何工作订单的状态不能超过一个,并且检查约束确保它必须是已拒绝、已交付或已取消之一。如果将 WO 状态定义为,例如,拒绝,则必须在状态表中创建一个值为“R”的条目。因此,只能为该 WO 生成拒绝表中的相应条目(它只能包含值“R”)。 Delivered 表中的条目(其状态值只能为“D”)将被系统拒绝。
从而保持数据完整性。不幸的是,数据完整性经常在设计结束时被硬塞进去。我认为这应该是主要的设计原则。
当然,改进是可能的。例如,Status 表的 Value 字段本身可以是查找表的外键,而不是使用检查约束。这样可以方便地在以后添加新的状态值。