6

给定一个模型表“A”,它可以有多个子模型“B”,其中“B”将有一个或多个子模型“C”。这听起来很简单,但是我需要为每个“A”强制执行',任何“B”都必须有一个唯一的“C”集合。例如,C 不能是属于同一父“A”的两个“B”的子代。但“C”可以是子代鉴于每个 'B' 的父 'A' 是不同的..

这有意义还是我应该不混淆我的场景?提前喝彩!

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

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

只是为了澄清情况,我将解释这个场景,但这里有一些注意事项:

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


这是真实的故事:

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

因此,每个简报,一个成员只能提交一个提交,而一个提交可以有多个成员(合作者)。

希望这会有所帮助,但我认为您已经给了我很多帮助!

史蒂夫。

4

5 回答 5

3

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

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

该表示法使用 {} 主键指示符。所以 As 可以有多个 B(通过在 B 上放置 AID),B 可以有 Cs(通过使用多对多表 B_C_Link),并且多个 C 不能属于同一个 A(通过将 AID 添加到多-对多表和强制(AID,CID)唯一性。

于 2009-03-17T12:41:59.467 回答
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,以及与 B 关联的 C 的集合对于 A1可以不同于与 A2 的 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,要求给定 C_ID 在完整 A 中出现的次数为 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”必须在其子项可以附加之前存在,这没有任何意义。数据库目前允许在没有至少一个“C”的情况下将“B”附加到“A”。孩子,我将修改“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(第二个变体)中嵌入的相同查询。您也可以挖掘出额外的信息(简短标题、提交标题、成员名称、各种日期等),但问题的核心是显示的查询必须不返回任何数据。

于 2009-03-17T13:22:47.717 回答
0

将TableA的ID添加到TableB,并添加到主键,对TableB和TableC做同样的事情。

编辑:

我相信这个答案的第一部分将适用于 A 到 B 约束。但是,然后我会在 B 和 C 之间放置一个链接表,该表也包含 A 的 PK。这样你在 A:B 之间就有了一个 1:N,然后你的约束就会被强制执行。

于 2009-03-17T12:03:31.120 回答
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]}, ...)

主键在大括号中,外键在括号中。

在这里查看更多信息。

于 2009-03-17T12:09:59.403 回答
-1

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

于 2009-03-17T12:05:44.657 回答