我有一个表需要链接三个单独的表中的一个,但它只应链接到其中一个,例如。
Main_Table
id UNIQUEIDENTIFIER
t1_id UNIQUEIDENTIFIER
t2_id INT
t3_id INT
T1
id UNIQUEIDENTIFIER
name VARCHAR(255)
T2
id INT
name VARCHAR(255)
T3
id INT
name VARCHAR(255)
是否有可能有一个约束,即t1,t2或t3中的任何一个在任何时候都不为空?
这只是糟糕的设计吗?如果是这样,你会对设计提出什么建议?
修改
我被要求详细说明这种特殊设计背后的原因。
Main_Table试图成为付款人表,可以引用单个用户(T1),一组个人用户(T2)或一组组(T3)。
这是我继承的数据库设计,不幸的是它实际上不会发生变化。
我最大的问题是我需要在不同类型之间关联,因此类型字段在这里不起作用,因为索引不同。
答案 0 :(得分:4)
您所描述的设计称为独占弧。是的,这是一个非常脆弱的设计,甚至没有一些规范化规则。
这是另一种选择:
Main_Table
id UNIQUEIDENTIFIER
t_id INT NOT NULL
FOREIGN KEY (t_id) REFERENCES T0 (id)
T0
id UNIQUEIDENTIFIER
type INT NOT NULL CHECK (type IN (1,2,3))
UNIQUE KEY (id, type)
T1
id INT
type INT NOT NULL CHECK (type = 1)
name VARCHAR(255)
FOREIGN KEY (id, type) REFERENCES T0 (id, type)
T2
id INT
type INT NOT NULL CHECK (type = 2)
name VARCHAR(255)
FOREIGN KEY (id, type) REFERENCES T0 (id, type)
T3
id INT
type INT NOT NULL CHECK (type = 3)
name VARCHAR(255)
FOREIGN KEY (id, type) REFERENCES T0 (id, type)
使用此设计,Main_Table
中的每一行都必须引用T0
中的一行
同样,T0
中的每一行都可以是T1
,T2
或T3
中仅一行的父级。
这是一种在不破坏参照完整性的情况下实现类表继承和多态关联的方法。
Main_Table正试图成为付款人 表,可以引用任何一个 个人用户(T1),一组 个人用户(T2),或一组 团体(T3)。
是的,所以在面向对象设计方面要考虑到这一点。如果您有三个可以充当付款接收者的类,则可以创建一个名为Payable
的接口,以便您可以依赖于键入这些对象。例如,所有Payable
个对象必须具有sendPayment()
方法。在某些OO语言中,接口是一个超类,称为抽象类或纯虚拟类。
T0
表作为每个子表T1
,T2
和T3
的公共类型。当Main_Table
具有T0
的外键时,就像说Main_Table
必须引用某个Payable
的实体,但是从该超类下降的任何对象都可以使用
type
列只是一个技巧,可以确保一次只能由一个子类表引用给定的T0.id
。如果你可以依赖你的应用程序逻辑将一个给定的子行插入到一个子类表中,那么它是可选的。
另请参阅我的演示文稿中的多态关联部分“SQL Antipatterns Strike Back。”
答案 1 :(得分:1)
如果你的数据库有检查限制,你可以掀起一个丑陋的kludge,如:
ALTER TABLE Main_Table
add constraint CK_ThisWorksButItsUgly
check ( ( case when t1_id is null then 0 else 1 end
+ case when t2_id is null then 0 else 1 end
+ case when t3_id is null then 0 else 1 end) = 1)
那里的一些语法可能有误,但你明白了。它可能表现得还不错 - 只有在其中一个列被修改后才能触发检查 - 但是没办法它很漂亮。
Bill Karwin的Exlusive Arcs非常酷,如果你可以重新设计数据库设计。
答案 2 :(得分:0)
除了糟糕的设计,如果你不能改变它,可以使用触发器来强制执行这种约束
答案 3 :(得分:0)
这是gen-spec模式的另一个例子。
关于“泛化专业化关系建模”的Google网络文章。那里有一些很棒的。