棘手的关系数据库设计问题

时间:2009-02-11 16:59:13

标签: database-design relational

我有一个棘手的问题,我现在已经搞乱了几天,无法找到最佳解决方案。

这些是我的表格:

  • 位点
  • site_node

站点节点表包含表示层次结构的节点列表(使用嵌套集)。每个节点必须具有一个或多个关联页面。每个站点必须有一个关联的错误页面和一个未找到的页面。

因此,页面必须属于某个节点,或者网站必须属于错误或未找到页面。我目前正在解决的解决方案是:

  1. 页表上的parentType和parentId字段,其中type可以是“node”,“site_error”或“site_notFound”,id可以是站点或节点id(与该类型相关的任何一个)。
  2. 页表上的nodeId字段可以为null,然后是站点表上的errorPageId和notFoundPageId字段。
  3. 选项#1确保每个页面属于一个且只有一个其他实体,尽管实际上强制执行关系,因为parentId字段可以指向多个位置。

    选项#2更干净,但它基本上说该网站“属于”两个错误而未找到的页面,这可能是不好的做法。

    有任何想法或建议吗? 谢谢,
    千斤顶

5 个答案:

答案 0 :(得分:3)

为错误或未找到页面创建虚拟站点节点。您可以根据第一个选项将它们标记为特定类型的节点。这将使得更容易制作通用处理程序机制。它还将使连接更简单,这将有助于数据库查询性能。此外,它允许您添加更多类型的“特殊”页面(可能是登录屏幕)或使其可配置,而无需修改数据库架构。

答案 1 :(得分:2)

  

选项#1确保每个页面   属于唯一的一个   实体,虽然关系不好   实际上是作为parentId强制执行的   字段可以指向多个   的地方。

右。就关系理论而言,问题是您的“parentId”列违反third normal form,因为其含义因行parentType(非关键列)中的每行而异

如果单个列可能包含某人的电话号码他们的每一行的生日日期,则根据其他标志,您将无法使用设计合理的数据库。这是关于这个人的两个不同的事实,他们每个人都应该拥有自己的专栏。同样,将site_id或node_id存储在单个列中也会遇到同样的问题。

另一个线索是,这是一个有缺陷的设计,你不能声明外键约束指向两个引用表的

  

选项#2更清洁,但它是   基本上说是网站   “属于”两个错误而不是   找到了页面,这可能很糟糕   实践。

我明白为什么你这么说,因为属于Rails类框架中的约定。但这些都是惯例;它们不一定是外键可以建模的唯一关系。您可以使一个实体引用另一个实体,在中有一个关系。在这种情况下,外键反转方向。

我会说错误页面和未找到页面属于网站,而不是相反,这在逻辑上是正确的。并且强制它们的方法是让另一个实体引用这些页面,并将NOT NULL约束应用于这些引用。这就是你所描述的。

CREATE TABLE site (
  . . .
  error_page_id     INT NOT NULL,
  notfound_page_id  INT NOT NULL,
  FOREIGN KEY (error_page_id)    REFERENCES pages (page_id),
  FOREIGN KEY (notfound_page_id) REFERENCES pages (page_id)
);

这符合您的迫切需求,可以强制执行,并且处于正常形式。


@NXC suggests为错误和未找到页面制作虚拟节点。虽然这允许将这些节点存储在节点层次结构中,但它无法强制网站必须具有这些页面。也就是说,站点可以存储,而不会引用这些节点。

@Tony Andrews suggests在每个页面site_idsite_node_id中存储两列,并添加CHECK约束以确保其中一个非NULL。这似乎比parent_id / parent_type选项更好,但它仍然没有提供任何强制执行,每个站点必须有一个错误和一个未找到页面。

答案 2 :(得分:0)

另一个选择是有2列site_id和site_node_id,如下所示:

create table pages
 ( page_id ... primary key
 , site_id references sites
 , site_node_id references site_nodes
 , ...
 , constraint site_or_node check (  site_id is null and site_node_id is not null
                                 or site_id is not null and site_node_id is null
                                 )
 );

现在,您可以使用参照完整性来确保每个页面都属于某个站点或节点,而不是两者都属于。

答案 3 :(得分:0)

选项2更有意义,如果出现更复杂的并发症,将在以后保存你的大脑。站点与错误/未发现页面的一对一关系使其非常适合外键约束。

答案 4 :(得分:0)

选项1的修改。

包括两个单独的列,ParentNodeID和ParentSiteID。根据具体情况,将这两列中的一列保留为NULL。现在,您仍然可以为每个外键声明外键(引用)约束。

我真的不了解SiteNotFound案例。在这种情况下你能将两个外键都留空吗?

您的加入和搜索会更简单。你也将坚持1NF。这不是巧合。

您的选项1将来自不同域的值组合在一个字段中。这是糟糕的现场设计,IIRC违反了1NF。