我们有一个网络应用程序,用户可以在这里进行在线考试。
考试管理员将创建一份问卷。问卷可以有很多问题。每个问题都是一个多项选择题(MCQ)。
让我们说管理员创建一个包含10个问题的调查问卷。用户尝试这些问题。现在,与真实考试不同,用户可以多次尝试单一问卷。我们必须跟踪他的所有尝试。
e.g。
User_id Questionnaire_id question_id answer attempt_date attempt_no
1 1 1 a 1 June 2013 1
1 1 2 b 1 June 2013 1
1 1 1 c 2 June 2013 2
1 1 2 d 2 June 2013 2
现在也可能发生这样的情况:用户尝试过两次相同的问题后,管理员可以从同一个问卷中删除一个问题,但是用户尝试历史记录仍然应该参考,以便用户可以在他的尝试历史中看到他的那个问题。管理员删除该问题。
如果用户现在尝试更改此问卷,他应该只看到1个问题。
User_id Questionnaire_id question_id answer attempt_date attempt_no
1 1 1 a 3 June 2013 3
此外,在此用户修改了部分问题后,用户尝试历史记录应在修改前显示问题,而任何新尝试都应显示已修改的问题。
我们如何在数据库级别管理这个?
我的第一感觉是,
对于删除操作,请勿进行物理删除,只需使问题处于非活动状态,以便历史记录仍能跟踪用户的尝试。
对于修改,请为问题创建版本,每次新尝试都会刷新每个问题和历史记录的最新版本,并在尝试时保留对问题版本的引用。
答案 0 :(得分:3)
(对不起,我使用“考试”代替“问卷” - 后者对我的图表来说太笨拙了。)
是的,您必须进行某种形式的版本控制。隔离的版本控制对象很容易,但在对象之间进行版本控制链接可能会很快变得复杂。为了保持它(相对)简单,你可以这样做:
这形成了一个层次结构,可以自下而上地对其进行版本化以响应变化:
这种版本化的“分层”方法,即响应任何树节点的修改而创建整个树的新版本,可能有点浪费,但推理和实现起来相当简单。
另一种方法是显式版本化子对象和链接,然后仔细查询,以便仅检索特定的“及时快照”。这显然需要一个明显不同的数据库模式...
顺便提一下,您会注意到识别关系和生成的复合主键的大量使用。这对于正确建模USER_ANSWER底部的菱形依赖性是必要的,因此用户无法提供不属于考试的答案。
QUESTION.ANSWER_NO有助于识别正确的答案(这假定为MATCH SIMPLE个外键 - 另请参阅this post)。
ATTEMPT.TIMESTAMP是用户开始尝试的日期/时间。连同给出个别答案的时间,可以重新创建完整的“时间表”。考试时间是尝试开始和最后一个答案之间的时间。
USER_ANSWER.ANSWER_NO将保留在主键外,因此在同一次尝试中,不能为同一个问题提供两个不同的答案。
DELETED标志存在于考试版本中,而不是考试本身,因此如果您取消删除考试,将会有一个历史记录。您甚至可以多次删除和取消删除相同的考试。