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

Database design 数据库设计和外键:应该在相关表中的何处添加它们?,database-design,performance,foreign-keys,Database Design,Performance,Foreign Keys,我的数据库中有一个相对简单的表子集,用于跟踪所谓的会话。这些是学术会议(想想某个特定课程的内容)。表示会话信息的表格包括: sessions session_terms session_subjects session_mark_item_info session_marks 所有这些表都有自己的主键,就像一棵树,会话有术语,术语有主题,主题有标记项等等。因此,每一个表都至少有它的“父”外键 我的问题是,在设计方面,将会话主键作为外键包含在其他表中是一个

我的数据库中有一个相对简单的表子集,用于跟踪所谓的会话。这些是学术会议(想想某个特定课程的内容)。表示会话信息的表格包括:

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

我的问题是,在设计方面,将会话主键作为外键包含在其他表中是一个好主意,以便轻松选择相关会话项,还是冗余太多?

如果我在所有表中包括会话外键(或从表到继承人的所有父外键),我可以轻松地选择会话的所有标记。例如

SELECT mark FROM session_marks WHERE sessionID=...
WHERE something IN (SELECT...
如果我不这样做,那么我必须将选择与类似的东西结合起来

SELECT mark FROM session_marks WHERE sessionID=...
WHERE something IN (SELECT...
哪种方法“更正确”或更有效


提前谢谢

第二种方法更正确。实际上,要获取连接表的会话信息,不要害怕连接,这就是关系数据库的全部意义。你不想重复你自己(正常化)。因此,只保留对父对象的引用,而不保留对parent.parent的引用

对于初学者来说,这个问题经常出现,他们认为在子表中创建相同的键将使他们的生活更轻松,因为这样一来,select就可以变成:

SELECT blah FROM MyTableSubSub WHERE SessionID=340
问题是您在表中引入了重复数据,这些表可能不需要知道它们的父级。事实上,在您的设计中,您可以通过连接到另一个表来找到这些信息。例如:

SELECT blah FROM MyTableSubSub mtss INNER JOIN ParentTable p ON p.ID = mtss.ID...
到达该点后,就可以在父表中找到会话id。因此,不要重复表中相关的列

我的问题是,在设计方面,将会话主键作为外键包含在其他表中是一个好主意,以便轻松选择相关的会话项,还是冗余太多

就设计而言,这将添加冗余数据,并会中断。通常,您不应该仅仅为了避免联接而添加冗余字段


你所描述的叫做。在某些情况下,非规范化有助于掩盖关系数据库软件固有的低效,但这会带来许多权衡,在绝大多数情况下,这些权衡往往更为重要。

你的直觉是好的。您当然不希望有太多(或任何)多余的外键来混淆事情。应避免冗余数据。然而,在许多项目中,有几个或少数地方冗余数据可以真正简化其他地方的事情。如果这是一个曾经复制过的外键,而您在其他地方获得了很多简单性,那么就这样做吧。如果你真的走这条路,那么你的挑战就是把它弄清楚。你可以做两件事:

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

但请记住,只要索引良好,这些现代关系数据库就擅长连接。只有在使事情变得更简单的情况下才能这样做。

最好的设计方法是让每个表都有一个指向其直接父表的外键引用。 将所有父项的键放在表上方只会导致冗余,这是不可取的。 如果您注意索引表(在这种情况下,您不需要索引,因为它是主要的索引),那么您对性能的担心就可以消除了 您在下表中引用的键)

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

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>
从会话标记中选择*
来自会话、会话\术语、会话\主题、会话\标记\项目\信息、会话\标记
哪里
session\u marks.parent\u id=session\u mark\u item\u info.id
和session\u mark\u item\u info.parent\u id=session\u subjects.id
和session\u subjects.parent\u id=session\u terms.id
和session\u terms.parent\u id=session.id
和session.id=

由于联接位于索引(实际上是主)列上,因此它们将非常快。你不必担心表现。

我同意之前所有的帖子(那里有很多投票)

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

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
(如果父项并不总是具有子项,则使用左外部联接。)

例如,要获取给定会话项的mark_item_信息,您可以运行以下命令

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%确信,视图中提到但在查询中未实际使用的表将从执行计划中“删除”,因为它们不是必需的。

+1感谢您提供的规范化和非规范化参考。非常感谢您的回答。你的和机器的很难接受。你说得对,我是一个拥有大型数据库的初学者,我有点害怕加入;)感谢所有优秀的见解。我觉得