表关系的最佳模式设计,可强制执行完整性

时间:2009-03-17 11:58:49

标签: database schema-design

给出一个模型'A'表,可以有多个子模型'B',其中'B'将有一个或多个子模型'C'......这听起来很简单,但我需要强制执行每个'A',任何'B'必须有一个独特的'C'集合..例如,C不能是两个'B'的孩子,它们是同一个父母'A'的一部分......但是'C'可以因为每个'B的父母'A'是不同的,所以要成为多个'B'的孩子。

这是否有意义,还是我应该对我的方案进行不模糊处理?提前喝彩!

请注意,我知道此策略将在应用程序中强制执行,但我不希望数据库无法处于无效状态。

编辑:大家好,很棒的反馈,所以我首先要感谢大家与我分享你的知识。

为了澄清情况,我将解释一下情况,但这里有一些注意事项:

'A'具有零个或多个'B','B'隐含地与'A'相关联,因此总是只有一个'A'的孩子。 'C'在某种程度上是与许多'B'以及数据库中的其他元素相关联的根实体。


这是真实的故事:

这是一个包含许多简报(A)的网站,许多成员(C),简报可以有很多提交(B),其中提交将始终有一个或多个相关成员。提出的意见是,提交实际上可以是一种协作,每个成员都没有比其他成员更多的“权力”,但会有一个不同的系统来验证成员如何一起工作的政策。

因此,简而言之,成员只能提交一份提交内容,而提交内容可以有很多成员(合作者)。

希望有所帮助,但我想你已经给了我很多帮助了!

史蒂夫。

5 个答案:

答案 0 :(得分:3)

我想我已经在这里捕获了你的关系模型;如果没有,那么我投票支持非混淆:

  • A [{AID},...]
  • B [{BID},AID,...]
  • C [{CID},...]
  • B_C_Link [{BID,CID},AID]
    • (AID,CID)上的附加唯一索引

符号使用{}主键指示符。因此,由于可以有多个B(通过在A上放置AID),Bs可以有Cs(通过使用多对多表B_C_Link),并且多个Cs不能属于相同的A(通过将AID添加到多个A- to-many table和enforcing(AID,CID)唯一性。

答案 1 :(得分:3)

我认为您需要SQL标准断言,(很遗憾)很大程度上没有实际的DBMS实现。

所有答案都同意有三个名为TableA,TableB和TableC的主表,每个表都包含自己的ID列:

TableA (A_ID PRIMARY KEY, ...)
TableB (B_ID PRIMARY KEY, ...)
TableC (C_ID PRIMARY KEY, ...)

从问题的描述中不清楚单个B值是否可以具有多个A父条目。很明显,单个C可以有多个B父条目。如果B绑​​定到单个A,则可以将TableB的设计修改为:

TableB (B_ID, ..., A_ID REFERENCES TableA)

如果B可以与几个不同的A相关联,那么连接最好用连接表来表示:

A_and_B (A_ID REFERENCES TableA,
         B_ID REFERENCES TableB,
         PRIMARY KEY (A_ID, B_ID)
        )

从描述中还​​不清楚与B相关联的C是否必须与B相关联的每个A相同,或者不同的A是否可以引用相同的B,以及与C相关联的C的集合是否相同。 A1的B可以不同于与B的B相关的C的集合。 (当然,如果单个B只能与一个A相关联,则此问题没有实际意义。)

出于本答案的目的,我将假设任何B与单个A相关联,因此TableB的结构包括A_ID作为外键。由于单个C可以与多个B相关联,因此相关结构是一个新的连接表:

B_and_C (B_ID REFERENCES TableB,
         C_ID REFERENCES TableC,
         PRIMARY KEY (B_ID, C_ID)
        )

简化(通过省略关于延迟性和即时性的规则)断言看起来像:

CREATE ASSERTION assertion_name CHECK ( <search_condition> )

因此,一旦我们有一组设计决策,我们就可以编写一个断言来验证数据。给定表TableA,TableB(带有外键A_ID),TableC和B_and_C,要求是完整A上给定C_ID的出现次数为1。

CREATE ASSERTION only_one_instance_of_c_per_a CHECK
(
     NOT EXISTS (
         SELECT A_ID, COUNT(C_ID)
             FROM TableB JOIN B_and_C USING (C_ID)
             GROUP BY A_ID
             HAVING COUNT(C_ID) > 1
     )
)

[修改:我认为这更准确:

CREATE ASSERTION only_one_instance_of_c_per_a CHECK
(
     NOT EXISTS (
         SELECT A_ID, C_ID, COUNT(*)
             FROM TableB JOIN B_and_C USING (C_ID)
             GROUP BY A_ID, C_ID
             HAVING COUNT(*) > 1
     )
)

连接条件的集合随表格连接的其他规则而变化,但总体约束结构保持不变 - 对于特定A_ID,不得存在对给定C_ID的多个引用。


在下面的评论中,meandmycode注意到:

  

我觉得我的设计存在缺陷。我的现实世界逻辑是'B'总是至少有一个孩子'C'。这是没有意义的,因为'B'必须存在才能附着它的孩子。   数据库目前允许'B'被附加到'A'而没有至少一个'C'..孩子,我就是这样修改'B'以便它有一个字段来引用它小孩'C',以及有一个额外'C'的子集合,但现在我有一个集合,也可能包括'B'指定的主要'C',这将是..错误。

     

是否存在可以推断出“一个或多个孩子”规则的数据库模式,而不是零或更多?

我认为您的模型确实存在问题。如果必须已经存在引用新创建的B的C,则很难创建B,特别是如果C必须仅引用现有的B.我想到了“鸡肉和鸡蛋”这个词。所以,通常情况下,你允许B在这样的上下文中有零个或多个C.

您仍未规定TableB是否具有A_ID外键,或者您是否具有类似A_and_B的链接表。如果它有一个外键,那么在你创建它所引用的A之前,你可能无法创建一个B.

我认为在表B中包含一个C ID并不是一个好主意 - 它会导致非对称处理(更难的SQL)。这也意味着如果你需要删除那个C,你必须更新一些东西,以便从当前所在的表中删除其他一个C引用,然后更新B记录中的值。这很麻烦,要有礼貌。

我认为您需要修改您的问题以定义您正在查看的实际表格结构 - 沿着各种答案中显示的行;您可以使用三点来表示其他但不相关的列。我建议的断言可能必须实现为某种触发器 - 它会进入DBMS特定的表示法。


从简要说明(A),提交(B)和成员(C)的修订说明中,很明显单个提交仅适用于一个简报,因此提交的内容可以使用简单的外键来标识简要说明是一个提交。并且会员只能针对特定简报进行一次提交。将有一个'submission_collaborators'表,其中包含用于标识提交和成员的列,组合是主键,每列都是外键。

Briefs(Brief_ID, ...)
Submissions(Submission_ID, Brief_ID REFERENCES Briefs, ...)
Members(Member_ID, ...)
Submission_Collaborators(Submission_ID REFERENCES Submissions,
                         Member_ID REFERENCES Members,
                         PRIMARY KEY (Submission_ID, Member_ID)
                        )

因此,要求是以下查询必须不返回任何行:

SELECT s.brief_id, c.member_id, COUNT(*)
    FROM submissions AS s JOIN submission_collaborators AS c
         ON s.submission_id = c.submission_id
    GROUP BY s.brief_id, c.member_id
    HAVING COUNT(*) > 1

这与我在CREATE ASSERTION(第二个变体)中嵌入的查询相同。您也可以挖掘出额外的信息(简要标题,提交标题,成员名称,各种日期等),但问题的核心是所显示的查询必须不返回任何数据。

答案 2 :(得分:0)

将TableA的ID添加到TableB,并将其添加到主键,并为TableB和TableC执行相同的操作。

编辑:

我相信这个答案的第一部分将适用于A到B约束。但是,我会在B和C之间放一个连接表,它也持有A的PK。这样你在A:B之间得到1:N,然后强制你的约束。

答案 3 :(得分:0)

你所拥有的是三元关系。你需要做的是有一个表,它将A,B和C连接在一起作为主键。由于主键不能重复,因此每个A和每个B只能有一个C.这将创建您正在寻找的唯一集合。

您将获得以下表格结构:

A's({A_ID}, ...)
B's({B_ID}, ...)
C's({C_ID}, ...)
A_B_C_Relation({[A_ID], [B_ID], [C_ID]}, ...)

主键位于括号中,外键位于括号中。

查看here了解更多信息。

答案 4 :(得分:-1)

我认为你不能用简单的声明性参照完整性约束来做到这一点。强制执行逻辑的最佳方法可能是使用触发器来实现业务约束,并回滚任何违反规则的插入或更新。