我有一个棘手的问题,我现在已经搞乱了几天,无法找到最佳解决方案。
这些是我的表格:
站点节点表包含表示层次结构的节点列表(使用嵌套集)。每个节点必须具有一个或多个关联页面。每个站点必须有一个关联的错误页面和一个未找到的页面。
因此,页面必须属于某个节点,或者网站必须属于错误或未找到页面。我目前正在解决的解决方案是:
选项#1确保每个页面属于一个且只有一个其他实体,尽管实际上强制执行关系,因为parentId字段可以指向多个位置。
选项#2更干净,但它基本上说该网站“属于”两个错误而未找到的页面,这可能是不好的做法。
有任何想法或建议吗?
谢谢,
千斤顶
答案 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_id
和site_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。