【问题标题】:What's the best way to store the days of the week an event takes place on in a relational database?在关系数据库中存储事件发生的星期几的最佳方式是什么?
【发布时间】:2010-09-23 17:27:15
【问题描述】:

我们正在为学校编写记录管理产品,其中一项要求是能够管理课程安排。我还没有查看我们如何处理这个问题的代码(我目前在另一个项目中),但是我开始想知道如何最好地处理这个要求的一个特定部分,即如何处理这样一个事实每门课程可以在一周中的一天或多天举行,以及如何最好地将这些信息存储在数据库中。为了提供一些上下文,一个简单的Course 表可能包含以下列:

Course          Example Data
------          ------------

DeptPrefix      ;MATH, ENG, CS, ...
Number          ;101, 300, 450, ...
Title           ;Algebra, Shakespeare, Advanced Data Structures, ...
Description     ;...
DaysOfWeek      ;Monday, Tuesday-Thursday, ...
StartTime       
EndTime           

我想知道的是,在这个(人为的)示例中处理 DaysOfWeek 列的最佳方法是什么?我遇到的问题是它是一个多值字段:也就是说,您可以在一周中的任何一天上一门课程,而同一门课程可以在一天以上的时间内上完。我知道某些数据库本身就支持多值列,但是假设数据库本身不支持它,是否有“最佳实践”来处理这个问题?

到目前为止,我已经提出了以下可能的解决方案,但我想知道是否有人有更好的解决方案:

可能的解决方案 #1:将 DaysOfWeek 视为位字段

这是我脑海中闪现的第一件事(我不确定这是否是一件好事……)。在此解决方案中,DaysOfWeek 将被定义为一个字节,前 7 位将用于表示星期几(每天一位)。 1 位表示课程在一周中的相应日期举行。

优点:易于实现(应用程序可以处理位操作),适用于任何数据库。

缺点:更难编写使用DaysOfWeek 列的查询(尽管您可以在应用程序级别处理此问题,或在数据库中创建视图和存储过程以简化此操作),中断关系数据库模型。

可能的解决方案 #2:将 DaysOfWeek 存储为字符串

这与使用位字段本质上是相同的方法,但不是处理原始位,而是为一周中的每一天分配一个唯一的字母,DaysOfWeek 列只存储一个字母序列,指示星期几一个课程正在举行。例如,您可以将每个工作日与一个单字符代码相关联,如下所示:

Weekday      Letter
-------      ------

Sunday       S
Monday       M
Tuesday      T
Wednesday    W
Thursday     R
Friday       F
Saturday     U

在这种情况下,周一、周二和周五举行的课程将具有 'MTF' 的值 DaysOfWeek,而仅在周三举行的课程将具有 DaysOfWeek 的值 'W'

优点:在查询中更容易处理(即,您可以使用INSTR 或其等效项来确定某个课程是否在给定日期举行)。适用于任何支持 INSTR 或等效函数的数据库(我猜是大多数......)。使用DaysOfWeek 列的查询也更易于查看,并且一目了然。

缺点:唯一真正的“缺点”是,与位域方法一样,它通过在单个字段中存储可变数量的值来破坏关系模型。

可能的解决方案 #3:使用查找表(丑陋)

另一种可能性是创建一个新表来存储一周中所有日期的唯一组合,并将Course.DaysOfWeek 列简单地作为该查找表的外键。然而,这个解决方案似乎是最不优雅的一个,我之所以考虑它,是因为它看起来像是 The Relational WayTM 做事。

优点:从关系​​数据库的角度来看,这是唯一“纯”的解决方案。

缺点:不优雅且笨重。例如,您将如何设计用户界面以根据查找表将相应的工作日分配给给定的课程?我怀疑用户想要处理“周日”、“周日、周一”、“周日、周一、周二”、“周日、周一、周二、周三”等这样的选择......

其他想法?

那么,有没有更优雅的方式来处理单列中的多个值?还是建议的解决方案之一就足够了?对于它的价值,我认为我的第二个解决方案可能是我在此处概述的三种可能解决方案中最好的,但我很想知道是否有人有不同的意见(或者确实是完全不同的方法)。

【问题讨论】:

  • 我在回复中留下了一些关于域的警告。看看吧。

标签: sql database-design


【解决方案1】:

如果我们使用 bit 选项,我认为编写查询并不难。只需使用简单的二进制数学。我认为这是最有效的方法。就个人而言,我一直这样做。看看:

 sun=1, mon=2, tue=4, wed=8, thu=16, fri=32, sat=64. 

现在,假设课程在周一、周三和周五举行。保存在数据库中的值是 42 (2+8+32)。然后你可以像这样选择星期三的课程:

select * from courses where (days & 8) > 0

如果你想在周四和周五上课,你会写:

select * from courses where (days & 48) > 0

本文相关:http://en.wikipedia.org/wiki/Bitwise_operation

您可以将星期几作为常量放在代码中,这样就足够清楚了。

希望对你有帮助。

【讨论】:

  • 这正是他给出的第一个解决方案.. "可能的解决方案 #1:将 DaysOfWeek 视为位字段"
  • 我明白他说的是每周一天使用一个位字段,而不是整个字节。我又读了一遍,发现我真的误解了它。编辑了答案。谢谢指点。
  • github.com/vahidhedayati/grails-daysofweek-plugin 感谢@NelsonTeixeira - 只是对这一概念的扩展,以便从制作真正的工作日列表
  • @Vahid 哇!您根据我的回答制作了该库,还是只是在向我展示?
  • 很酷! 人类可读的解释:7 位,每个日期一个位,& => 按位和 => 101010 & 100000 = 100000。1&1 = 1 其余组合 0 (1&0 = 0, 0&0 = 0 ), => 每个日期组合类似于 1101001(星期日 (1)、星期三 (8)、星期五 (32)、星期六 (64))[从右到左]] =sum=> 105 = (1101001)(base 2) .如果没有匹配,则进行比较!按位 = 0(例如:1101001 & 0010110 = 0 (64 + 32 + 8 + 1) & (16 + 4 + 2)))!否则(至少一场比赛),betwise > 1!因为至少有一位是 1,所以 >= 1 => >1!
【解决方案2】:

为了纯粹的感觉,我会避免使用字符串选项:它添加了您不需要的额外编码/解码层。在国际化的情况下,它也可能会搞砸你。

由于一周的天数是 7,我会保留七列,也许是布尔值。这也将有助于后续查询。如果该工具曾经在工作周开始于不同日期的国家/地区使用过,这也很有用。

我会避免查找,因为那会过度规范化。除非您的查找项目集不明显或可能更改,否则它是矫枉过正的。在一周中的几天(例如,与美国各州不同)的情况下,我会在固定的设置下睡得香甜。

考虑到数据域,我认为位域不会为您节省大量空间,只会让您的代码更加复杂。

最后,关于该领域的一个警告:许多学校在他们的日程安排上做了一些奇怪的事情,他们“交换天数”以平衡每个学期中相同数量的工作日,尽管有假期。我不清楚您的系统,但也许最好的方法是存储预计课程实际日期的表格。这样一来,如果一周有两个星期二,教师可以因两次出现而获得报酬,而被取消的星期四的教师将不支付报酬。

【讨论】:

  • 我喜欢这里提出的许多解决方案,因为它们的范围从非常简单到涉及更多但非常灵活;这个答案很好地总结了频谱的两端:基本的“每天一列”与添加一个或多个额外表格以跟踪更详细的日程安排信息。
【解决方案3】:

可能的 #4:为什么它需要是单列?您可以将一周中的每一天的 7 位列添加到表中。针对它编写 SQL 很简单,只需在您选择的列中测试 1 即可。从数据库读取的应用程序代码只是将其隐藏在一个开关中。我意识到这不是正常的形式,我通常会花费大量时间来尝试撤销以前程序员的此类设计,但我有点怀疑我们是否会很快在一周内增加第八天。

要评论其他解决方案,如果遇到查找表,我可能会抱怨。我的第一个想法也是带有一些自定义数据库函数的位字段,以帮助您轻松地针对该字段编写自然查询。

我很想看看人们提出的其他一些建议。

编辑:我应该添加#3,上面的建议更容易添加索引。我不确定如何为不会导致表扫描的 #1 或 #2 查询编写 SQL 查询,例如“让我在星期四获得所有课程”。但我今晚可能只是昏昏沉沉的。

【讨论】:

  • +1 我喜欢每个工作日使用单独的列的想法。干净简单。至于它不是正常形式,我怀疑是否有一种方法可以通过完全规范化的设计来处理这个问题,这种设计最终不会变得一团糟(即像查找表一样),但也许这里有人知道得更好。 ..
  • 我认为这是唯一合理的解决方案。如果值中可能存在一些意想不到的变化,则查找是有意义的。我不知何故认为一周的天数很快就会改变,所以这样一个核心概念的固定列数是合法的。
  • 您如何编写诸如“每天有多少门课程”或“列出每周 3 节课的所有课程”之类的查询?这种解决方案不会使报告复杂化吗?
  • 这很好,文森特。最终,每个数据库设计都是一种权衡。我不认为它会影响报告,但会话编号的事情,你可能需要一些额外的功能或在那里查看。
  • 嗯......在特定的日子做一个select count和一个select blah where sum(mon, tues, wed, thurs, fri, sat, sun) = 3。没错,不干净,但我认为可能足够了。
【解决方案4】:

3 号解决方案似乎最接近我的建议。查找表概念的扩展。每门课程都有一个或多个课程。创建具有属性的会话表:course_id、day、time、lecturer_id、room_id 等。

假设您以后可能想要存储这些数据,您现在可以为每门课程的每个课程分配不同的讲师或房间。

如果您正在考虑最佳数据库设计,则用户界面问题无关紧要。您始终可以创建视图来显示数据,并且为了捕获数据,您的应用程序可以处理为每门课程捕获许多会话并将它们添加到数据库中的逻辑。

表格的含义会更清楚,这使得长期维护更容易。

【讨论】:

  • 我不喜欢我设计的查找表,但是按照你描述的方式有一个单独的会话表会更好,而且,就像你说的,可以保持数据库规范化.
  • 我实际上正在为一所学校开发一个解决方案,这与我们正在做的很接近,只是它要复杂得多,因为我们必须考虑到每个会话都可以移动或取消等我们想跟踪所有这些。
【解决方案5】:

如果您选择一个或两个,您的表将不是 1NF(第一范式),因为它包含一个多值列。

Nicholas 有一个绝妙的想法,尽管我不同意他的想法打破了第一范式:数据实际上并没有重复,因为每一天都是独立存储的。 唯一的问题是您必须检索更多列。

【讨论】:

    【解决方案6】:

    如果性能是一个问题,我会推荐 #3 的更干净的变体。

    将您的课程链接到“时间表”表。

    这又链接到 days_in_schedule 表。

    days_in_schedule 表包含 schedule_name 列和 in_schedule_day 日期。该计划中的每个有效日期都有一行。

    您需要一些时间操作一些聪明的程序来填充表格,但一旦完成 灵活性是值得的。

    您不仅可以处理“仅限周五课程”,还可以处理“仅限第一学期”、“第三学期关闭实验室进行翻新”和“加拿大分公司有不同的假期安排”。

    其他可能的查询是“从 4 月 1 日开始的 20 天课程的结束日期是什么”,“日程安排冲突最多”。 如果你真的很擅长 SQL,你可以问“对于已经预订了 yyy 课程的学生来说,课程 xxx 中可能有哪些日子开放”——我觉得这是你提出的系统的真正目的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-08-09
      • 1970-01-01
      • 1970-01-01
      • 2010-09-14
      • 1970-01-01
      • 2013-06-14
      相关资源
      最近更新 更多