我已经在这里和其他地方读过很多关于在mysql中存储数组的强有力的陈述。规范化规则似乎表明它是一个坏主意,在存储的数组中搜索会产生不优雅的代码。但是,对于我正在处理的应用程序来说,它似乎是一个合理的解决方案,可以在一个字段中存储数组。我敢肯定,每个人都错误地认为这个位置,但我无法找到更好的方法。这是设置:
我有一系列表格可以存储注册学生,他们可以参加的课程以及每门课程的表现。所有这些都被“标准化”以避免重复和错误。我希望能够生成一个“myCourses”部分,所以在登录后,学生可以看到他们有资格参加的课程和他们已经学过但可以自由复习的课程。想到的方法是两个数组; my_eligible_courses和my_completed_courses。在注册时,学生将获得一系列符合条件的课程。这可以存储为有多个studentid的行,每个课程可以使用一个:
student1课程1 student1课程2 student1 course n
然后可以查询所有学生1的符合条件的课程表,并在学生登录时显示为列表。
或者,studentid可以是主键,在“qualified_courses”栏中会有一个数组(课程1,课程2,课程n)。
有一个学生表现表,记录每个课程和与学生表现相关的指标。将询问报告学生的表现,课程质量等,但这个表将会变得非常大。我很难相信生成my_completed_courses列表的最有效方法是每次登录时都通过studentid查询此表,只是为了给他们一个已完成课程的列表。
另一个复杂因素是学生符合条件的课程集是可变的,随着新课程的开发而不断扩展,这对我来说似乎表明为每门新课程生成一组新栏目是一个坏主意 - 例如,new course_name,pretest_score,posttest_score,time_to_complete,...此外,每个新课程的表格对于生成一组简单列表的相对平凡的终点来说似乎是一个复杂的解决方案。
因此,为了重申这个问题,最好是将“不合格”排列的合格和已完成课程列表存储在已注册的学生表中,还是动态生成这些列表?
我猜这仍然太模糊,但是对db设计的任何讨论都给出了一个不优雅的数组与重组模式的例子,我们将不胜感激。
答案 0 :(得分:1)
您应该确信,如果您的表上有适当列的索引,查询my_completed_courses
将非常活泼。
当您的表增长到您注意到减速时,您可以使用适当的内存分配设置配置MySQL服务器,以便它可以将更多数据缓存在内存中。或者你现在可以调查一下。
响应您对添加新课程所做的编辑:不要为每门课程添加新列。不要为每个课程添加新表。为课程创建一个表,并为每个课程添加行。
然后,您应该能够在索引列上将表连接在一起,以生成所需的数据列表。
答案 1 :(得分:1)
这有两个显而易见的原因:
X 什么是阻止有错误的应用程序在数组中存储不存在的ID?或者删除学生仍然参考的课程?即使您的应用程序对删除课程非常小心,也无法有效地执行此操作 - 您需要使用全表扫描来检查所有数组。
为什么你甚至试过这个?链接(也称为联结)表可以解决这些问题,但需要额外的存储空间。
如果您真的担心存储空间,您甚至可以切换DBMS并使用支持前沿索引压缩的DBMS(例如Oracle)。
我很难相信生成my_completed_courses列表的最有效方法是每次登录时都通过studentid查询此表,以便为他们提供已完成课程的列表。
数据库非常擅长查询大量数据。在这种情况下,如果正确使用clustering,DBMS将能够在非常少的I / O操作中获取此数据,这意味着非常快。你有没有执行任何实际的基准测试?您是否测量任何实际的性能问题?
此外,每个新课程的表格对于生成一组简单列表的相对平凡的终点来说似乎是一个复杂的解决方案。
生成新表可以,以防它具有不同的列。但是,这听起来并不像你想做的那样。
在我看来,你只需要:
CHECK (
(COMPLETED = 0 AND (performance fields) IS NULL)
OR (COMPLETED = 1 AND (performance fields) IS NOT NULL)
)
(顺便说一下,你甚至可以完全省略COMPLETED,只需依靠测试性能字段为NULL。)
InnoDB tables are clustered,这意味着属于同一学生的STUDENT_COURSE中的行物理上紧密地存储在一起,这意味着获得给定学生的课程非常快。
如果您需要向相反的方向前进(获取给定课程的学生),请在相同的字段中添加索引,但顺序相反:{COURSE_ID,STUDENT_ID}。在这种情况下,您甚至可以考虑covering。
由于我们讨论的是少量行,因此将COMPLETED保留为未编入索引就可以了。如果您真的关注这一点,您甚至可以执行以下操作:
COMPLETED_STUDENT_COURSE是已完成课程的B-Tree (基本上是STUDENT_COURSE的子集,是所有注册课程的B-Tree)。
答案 2 :(得分:0)
以下是一些我认为可以帮助您做出正确决定的想法。
通常,使用正确规范化的表是一种规则。但可以是例外。也许您的项目可能
大多数时候,新开发人员倾向于专注于将数据导入数据库。在为特定目的检索它时,它们会卡住。因此,考虑到数组与关系表的两种情况,请问自己是否有任何一种方法可以达到您的目的。例如,如果您想列出学生X的课程,那么您的数组方法就可以了。这是因为您可以通过主键(如学生ID)检索它。但是如果你想知道有多少学生参加课程A,阵列方法将是一个可怕的方法。
然后,上述观点也将取决于您的数据量。例如,如果您只有大约一百名学生,您可能不会注意到性能上的差异。但是,如果您正在查看数千条记录并且您有大量的学生课程列表,则阵列方法
基准。这是您找到答案的最佳方式。您可以使用MySQL的解释,或者只使用执行查询的程序。尝试使用标准数据量的每种方法,看看哪种方法效果最好。例如,在最近的过去,MySQL吹嘘他们的ISAM引擎的力量。然后我不得不处理涉及数百万条记录的大型应用程序。在这里,我注意到每次有新记录进入时,必须重建索引。所以现在我们不得不改变规则。同样,您最好使用正确的数据量进行测试并做出更好的决策。
但是不要把这个例子作为规则。相反,遵循标准化的标准,只会弯曲例外规则。