你可以把任何你想要的东西放在一张桌子上。您给出一个声明/含义,并将使其正确的行放在表中。查询的含义是其行根据其表的含义而成立。这就是您使用数据库所需的全部内容。
约束来自给定的陈述和可能出现的情况。您声明它们以防止无法出现的状态。
当某些源列的值集必须是目标列的值集的子集时,存在“包含依赖性”约束。如果目标还必须形成其表的键,则存在“外键”约束。目标表是否为源表无关紧要。在 SQL 中有一种语法为“外键”的声明,但因为它的目标必须是唯一的,而不是键,它实际上只定义了一个外键。某些设计方法碰巧专注于某些约束,而忽略了其他约束甚至表含义。
你想到了一些关系(不一定体现在一个表中)
coupled(p,q) // == '[p] & [q] are coupled'
key {p,q}
// plus CHECK-expressible constraints only involving coupled
将这个表嵌入到另一个表中只是意味着这些约束将应用于嵌入表的适当部分。
你心里也有(不一定体现在表中)
userfact(p,...) // == '...[p] is such that ...'
key {p}
如果你声明
userbig(p,...,q) // == userfact(p,...) and coupled(p,q)
key {p} fk q->p
// plus coupled-like constraints involving select p,q from userbig
那么你不能记录关于未耦合的 p 的 userfact(p,...)。另外,您必须使用上述选择来查询独立于用户事实的耦合。
如果你让 q 为空,那么你得到
usernull(p,...,q) // == userfact(p,...) and (q is null and not exists q coupled(p,q) or not q is null and coupled(p,q))
key {p}
// plus coupled-like constraints involving select p,q from usernull where not q is null
不幸的是,现在您必须使用上面更复杂的选择来查询独立于用户事实的耦合。此外,对应于 fk q->p 的约束不再是 fk 约束而是更复杂,因为 q 可以为空。此外,任何时候您想要 usernull 的任何部分(甚至全部)都可能需要测试 q 的 nullness 以确保使用您想要的行。
最糟糕的是,SQL 运算符的行为很复杂,因为它是 3VL 类型的,所以除了极其简单的查询之外,所有涉及空值的查询的含义都非常复杂。它们不是表格含义的简单组合。应避免使用空值。
所以你真正想要的只是userfact 和coupled 及其fk p->userfact.p, q->userfact.p。
对于目前的 SQL DBMS,工业上必须不断选择以非声明方式约束性能并在某些表中使用空值(尽可能在查询表达式叶附近删除空值)。但是第一个应该设计得当。