数据库设计和外键:它们应该在相关表中添加到何处?

时间:2010-03-25 13:11:28

标签: database-design performance foreign-keys

我的数据库中有一个相对简单的表子集,用于跟踪称为会话的内容。这些是学术会议(想想特定计划的产品)。表示会话信息的表是:

sessions
  session_terms
    session_subjects
      session_mark_item_info
        session_marks

所有这些表都有自己的主键,就像一棵树,会话中有术语,术语有主题,主题有标记项等等。因此每个表至少会有“父”的外键。

我的问题是,设计方面明智的做法是将会话主键作为外键包含在其他表中以便轻松选择相关的会话项,还是太过分了? < / p>

如果我在所有表中包含会话外键(或来自层表中的所有父外键),我可以轻松选择会话的所有标记。例如,像

这样的东西
SELECT mark FROM session_marks WHERE sessionID=...

如果我不这样做,那么我必须将选择与

之类的东西结合起来
WHERE something IN (SELECT...

哪种方法“更正确”或更有效?

提前致谢!

5 个答案:

答案 0 :(得分:4)

第二种方法更为正确。实际上,为了获取会加入表的会话信息,不要害怕JOIN这是关系数据库的重点。你不想重复自己(规范化)。因此,您只能保留对父项的引用,而不是parent.parent。

对于初学者来说这个问题很多,他们认为在子子表中创建相同的键会让他们的生活变得更轻松,因为选择可以变成:

SELECT blah FROM MyTableSubSub WHERE SessionID=340

问题是你在表格中引入了重复数据,可能不需要知道父母的父母。实际上,在您的设计中的某个位置,您可以通过加入另一个表来查找此信息。例如:

SELECT blah FROM MyTableSubSub mtss INNER JOIN ParentTable p ON p.ID = mtss.ID...

到那时,您可以在父表中找到会话ID。所以不要重复相关表格中的列。

答案 1 :(得分:3)

  

我的问题是,设计方面明智的做法是将会话主键作为外键包含在其他表中以便轻松选择相关的会话项,还是太过分了?

在设计方面,这会添加冗余数据,并会中断normalization。通常,您不应该添加冗余字段以避免JOIN。

您所描述的内容称为denormalization。在某些情况下,非规范化有助于掩盖关系数据库软件固有的低效率,但这需要进行许多权衡,这在很多情况下通常更为重要。

答案 2 :(得分:3)

我同意所有先前的帖子(那里有许多赞成票)。

您可能考虑添加额外列的一个原因是,如果没有它们,您最终会编写大量冗余代码(五个表,四个内部联接,每个数据库调用一次或多次,yuk)。解决此问题的一个方法是创建一个“预加入”表的视图;有了这个,每次你不得不查询N级时,你不必重写N路连接。一些伪代码:

CREATE VIEW MyHierarchy AS
SELECT 
  sessions_id
  session_terms_id
  session_subjects_id
  session_mark_item_info_id
  session_marks_id
from sessions
inner join session_terms on ids
inner join session_subjects on ids
inner join session_mark_item_info on ids
inner join session_marks on ids

(如果父项不总是有子项,则使用左外连接。)

例如,要获取给定session_term的mark_item_info,您将运行类似

的内容
SELECT mii.*
 from MyHIerarchy mh
  inner join session_mark_item_info mii
   on mii.session_mark_item_info = mh.session_mark_item_info
 where mh.session_term_id = @session_term_id

因此节省了大量的编码时间。并且,检查查询优化计划,但我95%肯定这一点,视图中提到但未在查询中实际使用的表将从执行计划中“删除”,因为它们不是必需的。

答案 3 :(得分:1)

你的直觉很好。你当然不希望周围有许多(或任何)多余的外键来混淆事物。应避免冗余数据。然而,在许多项目中,有一些或几个小的地方,还原数据可以真正简化其他地方的事情。如果这是曾经重复的外键,并且你在其他地方获得了很多简单,那就去做吧。如果你选择这条路线,那么你的挑战就是明确。你可以做两件事:

  • 将这些超级外键放在每个表的字段列表中,因此稍后查看它的开发人员会得到一个暗示,这是额外的数据,而不是模型的真正的外键关系。
  • 将字段命名为不同的字段,而不是sessionID。叫它 。 。 。 ParentSession。将其称为SessionID将是一个问题。

但请记住,只要您的索引很好,这些现代关系数据库在加入时就会。只有这样才能使事情变得更简单。

答案 4 :(得分:0)

最好的设计方法实际上是让每个表都有一个对其直接父级的外键引用。 拥有桌子上方所有父母的密钥只会造成冗余,并且不可取。 如果您注意索引表,那么您对性能的关注可以消除(在这种情况下,您不需要索引,因为它是主要的 您在下表中引用的密钥。)

而且,使用WHERE IN(Select ..)会导致性能问题。 我建议你可以使用/自定义连接和我建议的模式,为数据库中的树创建任何类型的查询。 举个例子。 对于您在会话中找到sesssion标记的要求,您将编写

Select * from session_marks
from session, session_terms, session_subjects, session_mark_item_info,session_marks

Where 
session_marks.parent_id = session_mark_item_info.id
and session_mark_item_info.parent_id = session_subjects.id
and session_subjects.parent_id = session_terms.id
and session_terms.parent_id = session.id
and session.id = <some value>

由于连接在索引(实际上是主要)列上,因此它们将超快。你不必担心性能。