0

我在这里和其他地方已经阅读了许多关于在 mysql 中存储数组的强有力的声明。规范化规则似乎表明它是一个坏主意,并且在存储的数组中搜索会导致不雅的代码。但是,对于我正在开发的应用程序,将数组存储在字段中似乎是一个合理的解决方案。我敢肯定这是每个人在这个职位上的错误想法,但我想不出更好的方法。这是设置:

我有一系列表格来存储注册学生、他们可以参加的课程以及他们在每门课程中的表现。所有都“标准化”以避免重复和错误。我希望能够生成一个“我的课程”部分,以便学生在登录后看到他们有资格参加的课程以及他们已经参加但可以自由复习的课程。想到的方法是两个数组;my_eligible_courses 和 my_completed_courses。注册时,学生将获得一组他们有资格参加的课程。这可以存储为多个出现 studentid 的行,他们可以参加的每门课程都有一个:

学生 1 课程 1 学生 1 课程 2 学生 1 课程 n

然后可以在该表中查询学生 1 的所有符合条件的课程,并在学生登录时显示为列表。

或者,studentid 可以是主键,并且在“eligible_courses”列中会有一个数组(课程 1、课程 2、课程 n)。

有一个学生表现表,记录每门课程以及与学生表现相关的指标。它将被查询以报告学生表现、课程质量等,但此表会变得非常大。我很难相信生成 my_completed_courses 列表的最有效方法是每次登录时通过 studentid 查询此表,以便为他们提供已完成课程的列表。

另一个复杂情况是,学生有资格的课程集是可变的,并且随着新课程的开发而扩展,在我看来,这似乎表明为每门新课程生成一组新列是一个坏主意——例如,new course_name , pretest_score, posttest_score, time_to_complete, ... 此外,对于生成一组简单列表的相对平凡的端点来说,每门新课程的表格似乎是一个复杂的解决方案。

因此,为了重申这个问题,将合格和已完成课程的“不雅”排列列表存储在注册学生表中或动态生成这些列表更好吗?

我猜这仍然太含糊,但是任何关于数据库设计的讨论都将不胜感激。

4

3 回答 3

1

这是一个坏主意,原因有两个:

  1. DBMS 不能强制执行正确的引用X(可能还有)完整性,并且依赖应用程序级的完整性几乎总是一个坏主意
  2. 虽然数据库将能够回答查询:“根据给定的学生,给我课程”,但如果您需要,您将无法(有效地)朝相反的方向前进。

X如何阻止有缺陷的应用程序将不存在的 ID 存储在数组中?或者删除学生仍然参考的课程?即使您的应用程序对课程删除很小心,也没有办法有效地执行此操作 - 您需要进行全表扫描来检查所有数组。

你为什么还要尝试这个?链接(又名联结)表将解决这些问题,而成本适中的一些额外存储空间。

如果你真的很在意存储空间,你甚至可以切换 DBMS 并使用支持前沿索引压缩的 DBMS(例如Oracle)。

我很难相信生成 my_completed_courses 列表的最有效方法是每次登录时通过 studentid 查询此表,以便为他们提供已完成课程的列表。

数据库非常擅长查询大量数据。在这种情况下,如果正确使用集群,DBMS 将能够在很少的 I/O 操作中获取这些数据,这意味着非常快。您是否执行了任何实际的基准测试?您是否测量过任何实际的性能问题?

此外,对于生成一组简单列表的相对平凡的端点来说,每门新课程的表格似乎是一个复杂的解决方案。

生成一个新表可能是合理的,以防它有不同的列。但是,这听起来不像你想要做的。


在我看来,您只需要:

在此处输入图像描述

CHECK (
    (COMPLETED = 0 AND (performance fields) IS NULL)
    OR (COMPLETED = 1 AND (performance fields) IS NOT NULL)
)
  • 当学生注册课程时,在 STUDENT_COURSE 中插入一行,将 COMPLETED 设置为 0 并将表现字段保留为 NULL。
  • 当学生完成课程时,将 COMPLETED 设置为 1 并填写表现字段。

(顺便说一句,您甚至可以完全省略 COMPLETED,而只依赖于测试性能字段是否为 NULL。)

InnoDB 表是集群的,这意味着属于同一学生的 STUDENT_COURSE 中的行在物理上靠近存储在一起,这意味着获取给定学生的课程非常快。

如果您需要朝相反的方向前进(获取给定课程的学生),请在相同字段上添加索引,但顺序相反:{COURSE_ID, STUDENT_ID}。在这种情况下,您甚至可以考虑覆盖。

由于我们谈论的是少量行,所以将 COMPLETED 保留为未索引就可以了。如果您真的对此感到担忧,您甚至可以执行以下操作:

在此处输入图像描述

COMPLETED_STUDENT_COURSE 是用于已完成课程的 B-Tree(本质上是 STUDENT_COURSE 的子集,它是所有已注册课程的 B-Tree)。

于 2012-07-05T10:42:35.293 回答
1

您应该确信,如果您的表中有适当列的索引,那么查询my_completed_courses将非常快速。

当您的表增长到您注意到速度变慢的程度时,您可以使用适当的内存分配设置配置您的 MySQL 服务器,以便它可以将更多数据缓存在内存中。或者你现在可以调查一下。

针对您对添加新课程所做的编辑:不要为每门课程添加新列。不要为每门课程添加新表。为课程创建一个表,并为每个课程添加行。

然后,您应该能够在索引列上将您的表连接在一起,以生成您需要的数据列表。

于 2012-07-05T02:32:13.537 回答
0

以下是我认为可以帮助您做出正确决定的一些想法。

  1. 通常,使用正确规范化的表是一个规则。但这可能有例外。也许你的项目可能是这样的。

  2. 大多数时候,新开发人员倾向于专注于将数据放入数据库。在为特定目的检索它时,他们会陷入困境。因此,考虑到数组与关系表的两种情况,问问你自己,这两种方法是否符合你的目的。例如,如果你想列出学生 X 的课程,你的数组方法就可以了。这是因为您可以像学生证一样通过主键检索它。但是如果你想知道有多少学生在 A 课程上,那么数组方法将是一个可怕的方法。

  3. 再说一次,以上几点也取决于您的数据量。例如,如果您只有大约 100 名学生,您可能不会注意到表现的差异。但是,如果您正在查看数千条记录,并且您有大量针对学生的课程列表,那么数组方法不是要走的路。

  4. 基准。这是您找出答案的最佳方式。您可以使用 MySQL 的解释,也可以使用执行查询的程序对其进行计时。用您的标准数据量尝试每种方法,看看哪种方法效果最好。例如,在最近的过去,MySQL 一直在吹嘘自己的 ISAM 引擎的实力。然后我不得不处理一个涉及数百万条记录的大型应用程序。在这里,我注意到每次出现新记录时,都必须重建索引。所以现在我们不得不改变规则。同样,您最好使用正确的数据量进行测试并做出更好的决定。

但是不要把这个例子当作一个规则。相反,遵循规范化标准,只弯曲例外规则。

于 2012-07-05T02:55:43.043 回答