祖父母外键应该存储在孙子表中吗?

时间:2011-01-28 15:13:02

标签: sql entity-framework database-design orm foreign-keys

在表格中包括祖父母+外围钥匙有什么好处和责任。

例如,如果我的对象模型如下所示。 (非常简化,所以它不适用于层级递归表。)

a {aId, bCollection, ...}
b {bId, cCollection, ...}
c {cId, dCollection, ...}
d {dId}

我想到的两个数据模型选项是:

选项1:

a {pkA, ...}
b {pkB, fkA, ...}
c {pkC, fkB, ...}
d {pkD, fkC, ...}

选项2:

a {pkA, ...}
b {pkB, fkA, ...}
c {pkC, fkB, fkA, ...}
d {pkD, fkC, fkB, fkA, ...}

选项1更加规范化,插入和更新将更容易,但我可以看到查询变得非常复杂,特别是有很多关系和/或复合键。

选项2使插入和更新变得复杂,但提取报告将更容易。此外,数据库会更大,但我并不关心它,因为它非常小。

但与ORM类似实体框架的问题相比,这些问题相当微不足道。我倾向于选项2因为我想直接从父母那里访问孙子,所以:

Class A { id, bCollection, cCollection, dCollection, ... }
Class B { id, cCollection, dCollection, ... }
Class C { id, dCollection, ... }
Class D { id, ...}

实体框架4.0是否优雅地处理这种情况?这两种选择的优缺点是什么?还有其他我应该考虑的选择吗?

或者更简单的说,一个人如何谷歌这个问题?!?

另外一个注意事项:像你们中的许多人一样,必须对选项A有很大的帮助,但我知道我已经阅读了一篇msdn文章,该文章详细介绍了为什么选项B更好。不幸的是,我找不到它。 :(

提前感谢您的想法。

3 个答案:

答案 0 :(得分:6)

我会避免选择B像瘟疫一样。这两个选项的查询都不比另一个选项复杂得多,但第二个选项很多更难以维护,并且急于将选项A标准化。

对于查询选项A,您所谈论的只是添加简单连接。由于这不是一个递归关系,你必须已经预先知道查询可以“深入”多少级别,所以你不必担心它是脆弱的或只是为了一个潜在病例的子集。

比较您正在寻找深层嵌套节点的顶级父级的最深层案例:

选项A:

select
    a.id

from d

join c on c.id = d.c_id
join b on b.id = c.b_id
join a on a.id = b.a_id

where d.id = @id

选项B:

select a_id from d where id = @id

选项A 更多是否复杂?是的,但它不应该对任何人弄清楚发生了什么是一个挑战。

至于与EF4的兼容性,这不是问题。您可以以线性方式向上导航父母链,以获得您想要的祖父母。您可以在代码或查询中执行此操作;任何一个都可以正常工作:

在代码中:

var entity = context.D.Where(d => d.Id == 4).First();
var grandParent = entity.C.B.A;

在查询中(使用LINQ和连接):

var grandparent = (from d in context.D
                   join c in context.C on d.CId == c.Id
                   join b in context.B on c.BId == b.Id
                   join a in context.A on b.AId == a.Id

                   where d.Id == id

                   select a // or a.Id).First();

在查询中(使用导航属性):

var grandparent = (from d in context.D
                  where d.Id == id
                  select d.C.B.A // or d.C.B.A.Id).First();

答案 1 :(得分:3)

如果您的数据库主要是OLTP,我会使用选项A.如果您的报告出现问题,您可以创建一个视图或非规范化表来表示选项B.

如果你的数据库主要是OLAP,我会选择B。

答案 2 :(得分:3)

我想说如果数据库对递归关系有很好的支持(即Oracle很擅长这个,其他几个都有这些查询的特定语法),那么使用递归(你的选项A)。否则,您将被困几天调试代码,确保在关系发生变化时更新需要更新的所有记录,并且需要手动级联您的更改。

对谷歌而言,使用术语“递归表”或“递归查询”。