2016-09-28 93 views
2

考虑一个情况,我定义了一个对象,一组对象,则表链接在一起:外键表A或B表

CREATE TABLE obj (
    id INTEGER PRIMARY KEY, 
    name text 
) ; 

CREATE TABLE group (
    id INTEGER PRIMARY KEY ; 
    grpname TEXT 
) ; 

CREATE TABLE relation (
    objid INTEGER, 
    grpid INTEGER, 
    PRIMARY KEY (objid, grpid) 
) ; 

我找级联删除时适用,所以我添加外键

ALTER TABLE relation 
ADD FOREIGN KEY (objid) 
REFERENCES obj(id) 
ON DELETE CASCADE ; 

ALTER TABLE relation 
ADD FOREIGN KEY (grpid) 
REFERENCES group(id) 
ON DELETE CASCADE ; 

到目前为止都行。现在假设我想为群组添加支持。我想改变这样的关系表:

CREATE TABLE relation_ver1 (
    parent INTEGER, 
    child INTEGER, 
    PRIMARY KEY (parent, child) 
) ; 
ALTER TABLE relation_ver1 
ADD FOREIGN KEY (parent) 
REFERENCES group(id) 
ON DELETE CASCADE ; 

在这里,我得到了一个问题:我想申请级联删除对孩子太多,但我不知道这里是否孩子是指一组或对象。

可以向表obj或组添加外键吗?

我发现唯一的解决方案是增加child_obj和child_grp字段,添加相关的外键,然后,当插入例如一个对象时使用'特殊'(空类)组,然后做相反的事情插入子组。

+0

您的要求与关系理论中“关键”的概念不相容。你提到的解决方案已经很好了,我想。 – ultrajohn

+0

关于代理人的任何暗示? @ultrajohn – marom

+0

为了防止你的数据在relation_ver1表中稀少,我建议你将一个孩子是一个对象而一个孩子是一个组的情况分离到他们自己的表中。 – ultrajohn

回答

1

我们拥有外键的主要原因是而不是,以便能够执行级联删除等操作。存在外键的主要原因是参照完整性

这意味着grpid被声明为REFERENCES group(id)以确保grpid永远不会被允许采用组(id)中找不到的任何值。所以,这是一个有效性问题。级联的DELETE也归结为有效性:如果删除了一个密钥,那么引用该密钥的任何和所有外键都将失效,很显然,必须对它们做些事情。级联删除是一种可能的解决方案。将外键设置为NULL,从而消除关系,是另一种可能的解决方案。

你有一个孩子id的概念是指一个组或一个对象违反了参照完整性的任何概念。关系数据库理论没有用处,也没有提供多态性。一个关键必须指的是一个且只有一种实体。如果不是这样,那么你就会遇到像你刚才发现的那样的问题,但更糟的是,你不能在数据库中有任何参照完整性保证。这不是一个好的情况。

处理对不同类型实体的关系需求的方法是使用一组外键,每个可能的相关实体使用一组外键,其中只有一个外键非空。所以,这里是它会是什么样子:

CREATE TABLE tree_relation (
    parent_id INTEGER, 
    child_object_id INTEGER, 
    child_group_id INTEGER, 
    PRIMARY KEY (parent_id, child_object_id, child_group_id)); 
ALTER TABLE tree_relation 
    ADD FOREIGN KEY (parent_id) REFERENCES group(id) ON DELETE CASCADE; 
ALTER TABLE tree_relation 
    ADD FOREIGN KEY (child_object_id) REFERENCES object(id) ON DELETE CASCADE; 
ALTER TABLE tree_relation 
    ADD FOREIGN KEY (child_group_id) REFERENCES group(id) ON DELETE CASCADE; 

所有你需要做的就是确保只有一个child_object_idchild_group_id非NULL。

+0

嗯,不知何故比我的解决方法更好,因为我可以摆脱'空对象/组'。我可以在SQL中表达约束child_object_id或child_group_id不是NULL吗? – marom

+0

我不认为你可以,但我不确定这一切。这可能是一个不同的stackoverflow问题的一个很好的主题。 –

+0

通过SQL标准,您可以通过定义基于元组的“CHECK”来实现这一点。或者,在您的DBMS中不支持“CHECK”的情况下,您可以定义TRIGGER以实现相同的结果。 – ultrajohn

1

考虑关系:

relation_ver1(parent, child_obj, child_group) 

我宣称这关系有以下缺点:

  • 你必须处理NULL特例。
  • 约。值的1/3是NULL。 NULL值不好。

幸运的是,有一个简单的方法可以解决这个问题。由于数据中存在多值依赖关系,因此可以将表分解为2个较小的4NF兼容表。例如:

relation_ver_obj(parent, child_obj)relation_ver_grp(parent, child_group)