我现在正在玩数据库的想法。它可能不会以任何形式进行部署,而是更多的学习体验。
这是为了简化我去大学的一系列课程的导师信息的收集和处理。我在一个办公室做兼职,每个学期组织一些班级的导师。
我有很多问题,但目前导致我出现问题的是我如何存储每位导师的可用性。我现在正在考虑3个选项,我正在寻找从技术角度对每个选项的优缺点进行反馈。
背景 导师信息存储在“tutor”表中(tutorID引用此表),并且必须能够调用先前的可用性。导师可用性是离散的(每小时),并且在整个学期中保持不变。
选项1:
Table: Availability
+-----------+---------+-------+-------+---+---+---+----+---+
| avID (PK) | tutorID | year | sem | M | T | W | Th | F |
| | | (int) | (int) | (all strings) |
+-----------+---------+-------+-------+---+---+---+----+---+
在此表中,可用性存储在字符串中(08,09,10,13,14表示上午8点,上午9点,上午10点,下午1点和下午2点)。
可以使用
回收数据SELECT * FROM Availability WHERE tutorID=0001 AND year=2013 AND sem=1
并查看谁可用
SELECT * FROM Availability WHERE AND year=2013 AND sem=1 AND M LIKE '%08%'
选项2:
Table: Availability
+-----------+---------+-------+-------+--------------+
| avID (PK) | tutorID | year | sem | availability |
| | | (int) | (int) | (set) |
+-----------+---------+-------+-------+--------------+
在此布局中,可用性列在mysql中存储为SET数据类型,选项为Mon到Friday的每个组合,每次从8到4(M08,M09 ...... Th14,F16等等)。这可以达到45个可接受的值。这是我目前倾向于的那个,但我对SET数据类型知之甚少。
可以使用
回收数据SELECT * FROM Availability WHERE tutorID=0001 AND year=2013 AND sem=1
并查看谁可用
SELECT * FROM Availability WHERE AND year=2013 AND sem=1
AND FIND_IN_SET('M09',availability) > 0
选项3:
Table: Availability
+-----------+---------+-------+-------+-------+-------+
| avID (PK) | tutorID | year | sem | day | time |
| | | (int) | (int) | (int) | (int) |
+-----------+---------+-------+-------+-------+-------+
在此选项中,每个教师每年和每个时间段都有一行。
可以使用
回收数据SELECT * FROM Availability WHERE year=2013 AND sem=2 AND tutorID=0001
的可用性
SELECT * FROM Availability WHERE year=2013 AND sem=2 AND day=3 AND time=14
无论如何......感谢您阅读所有这些内容。希望有人能够对此有所了解。我认为它基本上归结为最佳实践类型的问题。除非有一些我完全错过的东西!!
答案 0 :(得分:3)
您列出的所有选项均不是normalized。基本规范化,以及关系数据库技术的一个主要观点和好处是避免存储冗余信息。
你不清楚这个要求,但我假设一个导师每天可以使用超过一个小时。这会使选项1变得笨拙或不合适,因为您必须有多行才能在一天内覆盖多个会话。其他列值将跨行重复 - 这种重复意味着违反规范化。
此外,选择文本作为开始时间的数据类型可能不是最佳选择。如果会话总是从小时开始,那么您正在处理小时数。如果处理数字,请将它们存储为数字(作为一般规则)。如果会话可能总是从小时开始,那么您正在处理时间值。相同的一般规则,将它们存储为时间数据类型。
选择int作为年份的数据类型可能并不清楚。通常学年就像“2013-2014”。
在选项2中,将多个数据点填充到单个字段中绝对不是标准化的。虽然您的查询可行但它至少有两个缺点。一个是表现;通常搜索这样的多值字段将相对较慢。但更重要的是,违反规范化几乎总会导致自己陷入困境。如果你想为这些时间段中的每个时间段增加额外的值,那该怎么办 - 你不能因为当它们被一起粉碎时你无法访问每个时段。
在选项3中,您越来越接近标准化设计。但请注意多个字段将如何重复(year
和sem
)?同样,这种重复是违反规范化的标志。
在设计时,一般来说,扩大或概括你的思维是一个好习惯。例如,会话始终将永远在小时开始并持续一小时吗?不见得。因此,使用Time值而不是小时数可能是明智的。另一个例子,“学期” - 并非所有学校都使用学期,甚至那些(你的)学校也可能会改变。因此,概括为“术语”并且不做与学期相关的假设可能是明智的。另一方面,不要过度概括,否则你可能陷入无意义的设计混乱或陷入分析瘫痪。
要规范化,寻找“事物”,可能采取行动的东西,或“拥有”其他东西的东西。我们称之为实体。
您已将tutor
标识为单独的实体。好。
我看到另一个:term
(学期)。重复'年'和'sem'是线索。通过将这些值移动到另一个表中来避免这种重复。该表适用于'term'的实体。单独表格正确的另一个线索是我们可能希望将其他信息与“期限”表联系起来,例如术语的开始日期和长度(或停止日期)。这些额外的数据当然不应该在我们所有的“可用性”行中重复。此类数据应在term
表中的一行中存储一次。
所以我的初始设计看起来像这个图。
此关系为Many-to-Many。每个导师可以多个术语提供,每个学期可以有多个导师。多对多是关系设计中的一个问题,始终使用第三个“桥”或“结点”表解析。在为业务环境设计的数据库中,多对多和桥接表非常常见。
这里,它们之间的桥接表是availibility_
。该桥表是两者的子表,并携带每个父键的主键(foreign key)。提示:当我将父母(蓝色在这里)放置得比儿童(这里是橙色)更高时,我注意到两侧父母的“鸟体有凸起的翅膀”模式,然后我认识到两者之间存在多对多的关系。父母。
顺便说一句,有时候违反正常化。我们称之为“去标准化”。通常,目标与绩效有关。但是,只有在您咨询了另一位经验丰富的数据库设计师之后才会进行非规范化处理,并且当您有充分的理由时,请清楚地了解您所支付的价格,并彻底记录违规行为,以便为那些可能在以后取代您的人提供启发。