选项 #1 确保每个页面属于一个且仅属于一个其他实体,尽管实际上不能强制执行关系,因为 parentId 字段可以指向多个位置。
对。就关系理论而言,问题在于您的 " parentId
" 列违反了第三范式,因为它的含义每行都会有所不同,具体取决于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建议为错误和未找到页面制作虚拟节点。尽管这允许将这些节点存储在节点层次结构中,但它无法强制站点必须具有这些页面。也就是说,可以在不引用这些节点的情况下存储站点。
@Tony Andrews建议在每个页面中存储两列,site_id
和site_node_id
,并添加一个 CHECK 约束以确保其中一个是非空的。这似乎比parent_id
/parent_type
选项更好,但它仍然没有强制要求每个站点都必须有一个错误和一个未找到页面。