数据库设计之问卷模块的设计2

数据库设计之问卷模块的设计1

2016/07/13更新

整个实习已经告一段落了,其实整个问卷模块的数据库设计,在6月初基本上已经最终敲定并实现出来了。

本次的总结分为两个部分。

一、对之前提出的几个遗留问题,解决了的做一个说明

二、最终总结一下,问卷模块的核心:问卷、试题、选项几个实体之间的关系如何表示,如实记录之中的心路历程,以及遇到过的一些坑。

一。之前遗留问题

1.结果试题标签

这个结果标签其实不是难点,到时候只需要记录获得的结果标签的id即可。

一次问卷的评价当然可以获得,若干个结果标签。同一个结果标签也可以被不同问卷所持有。

所以问卷:结果标签是n:n关系。

①可以使用关联表实现

②可以在评价结果记录中使用一个字符串来记录所有得到的结果标签id

这个标签模块,后来准备做成一个通用模块。

比较复杂的逻辑是如何根据用户的答案以及评价规则得到结果标签的id。

不过这不是本帖的内容。

2.试题序号

这个很多都可以由前端实现了。

但是当时我还是进行了实现的。

如果两个实体的关系是1:n,实现采用的是持有id实现,可以直接把序号维护在n的字段之中

比如,选项在试题中的序号(最终采用的是选项持有试题id)

如果两个实体的关系是n:n,实现采用的是关联表,那么序号应该放在关联表中

比如,试题在问卷中的序号

如果只是自身的序号,就直接放在自身字段之中

比如,问卷的序号、问卷类型的序号

①增

新增一个记录,序号赋值为,查询已有记录,然后count出来。

②删

删除一个记录,查询到序号大于被删除的记录的所有记录,对其序号字段进行批量减一操作。

③删除多个

在删除类型时,由于删除类型需要将其子类型一起逻辑删除,这个时候不确定会对哪些字段删除,不知道删除多少个。

采用的办法是,查询所有剩余的字段,按序号升序排序,然后统一遍历重新设定其序号。

3.填空题

未解决

4.父子试题、选项与隐藏试题

未实现

5.类型的删改策略

①删除采用逻辑删除

对字段N_IS_DELETE进行赋值。

删除后,新增加类型看不见已经删除了的。问卷选择类型也看不见已经删除了的。

但是已经选择了的,不会受到删除的影响。

特别注意,层级结构的话,删除需要对其子类别一同删除。

②编辑

内容当然可以编辑,但是层级结构不允许编辑


二。问卷模块的设计

最重要的就是三个实体:问卷(paper)、试题(question)、选项(option)

1.设计一


paper和question是关联表

option最开始的想法是,同一个题目,在不同的paper中,可以有不同的选项,并且拥有不同的分值。

当时采用的就是option持有paperId和questionId

优点:

①不同的试卷,对同一个题目的选项的修改是互不影响的

②同一个题目可以拥有不同的选项,提供了更大的灵活性

缺点:

如果有paper1和paper2都有试题6,这个时候paper3同时从paper1中导入了试题x,记为ques1,也从paper2中导入了试题6,记为ques2。

①选项窜了

对于paper3,ques1和ques2是不同的两道题。

然而,由于选项的设计,持有paperId和持有questionId,这个时候ques1的选项和ques2的选项共用了。

修改ques1的选项会完全反映到ques2上面。

这种情况虽然很难出现,但是由于我们就是希望提供统一题干,不同选项的灵活性,这个bug是实实在在存在的。

②导入试题不方便

这个时候导入试题,你会发现你只能导入题干,导入过来的试题,如果不指定试卷的话,导入过来的试题是没有选项的。

综合①、②我们只能修改设计。


2.设计二


如图所示

paper与question关闭不变

option牺牲一部分的灵活性与question绑定。

也就是说,除了分数以外,不同的试卷导入同一个试题,其题干以及所有选项内容是一样的。

这样试题和选项就是1:n关系,采用选项持有试题id即可。

由于还是需要维护不同试卷的同一试题同一选项的不同分值,

而试卷有多个选项,选项也可以对应不同的问卷,所以是n:n关系,所以这个时候问卷、选项也需要一张关联表。

该关联表字段有paperId、optionId、score

优点:

①选项与试题绑定以后,避免了一些麻烦,比如之前提到的串扰问题。

②导入试题的时候可以不用管问卷了,因为选项内容只与试题相关。

缺点:

①选项与试题绑定之后,也带来了新的缺点。不同的问卷导入同一道试题,会相互影响。

而这个影响本身也有好有坏。

如果希望对所有的问卷一起修改,当然很方便。

如果不希望别人的修改影响到自己,则无能为力。

②显示选项的分数有很大的麻烦。

A。通过问卷id,查询所有的试题id。通过某个试题id,查询所有的选项id,显示选项的内容。

B。通过问卷id,选项id在关联表中,查询选项的分数。

C。如果对于某个选项就需要查询一次数据库,代价太大。

如果查询问卷所有选项的分数,将分数与对应的想象匹配起来,异常地复杂。

解决办法:

针对②,解决办法是,修改问卷-选项关联表

将里面的字段改为paperId,questionId,optionName,score

这样将分数按选项的名称(A,B,C,D)单独显示在旁边,避免了配对的问题。

因为以选项名称来存储,所以无法区分不同试题的选项,所以又加入了questionId字段

针对①,开始我们想到了,先进行判断(通过在关联表中查询count某个试题,看是否等于1)。

如果某个试题没有被其他问卷导入,当然可以直接修改。

如果被多个问卷导入,那么将决定权给用户,在每次进行修改的时候告知用户这样修改可能会影响到其他试卷。


3.设计三

其实在设计二中,针对①的办法还存在着难以忍受的问题。

如果一个用户新建了一个题目,但是它还没有创建选项。

这个时候他有事儿走了,但是别的用户导入了该题目。

后来该用户回来开始创建选项,这个时候,它每创建一个选项,就会被问及这个修改会影响到别人,是否修改。

这个体验是不可忍受的。


在这种设计之下,试题和选项是绑定了的,

那么对于试题的修改就会有一下4种情况:

①对于试题内容的修改,比如修改题干。

②增加选项

③修改选项内容

④删去选项

而在这种不同问卷共享导入题目的设计有一个一直没有解决的矛盾:

对于一个人修改,所有人跟着变的共用
与自己所引用的东西,不希望别人修改影响到自己的不共用
共用与不共用的矛盾

最终我采用的折衷解决办法如下:

题目加入一个创建人字段
题目增加一个权限字段:①不可导入(这种权限的题目根本不会出现在导入列表中)②可导入,只能自己修改③可导入,任何人可以修改


权限编辑是单独的。只有当前操作人=题目创建人,可以进行权限编辑
普通编辑:
(1)若试题权限为不可导入,直接修改
(2)若试题权限为可导入,只能自己修改
①操作人=题目创建人,直接修改
②操作人!=题目创建人,弹出提示不能修改
(3)若试题权限为可导入,任何人可以修改,直接修改


导入分为两种:
(1)关联导入
导入直接将当前问卷与某个题目关联。复制问卷-试题-选项关联表记录即可。
关联导入的创建人不变。权限也不变。
(2)独立导入
复制一个问题,将其单独保存,再和当前试卷关联,再复制问卷-试题-选项关联表。
问题创建人修改为导入的操作人,权限默认修改为不可导入。

独立导入功能的加入,使得别人设置为可导入,只能创建人自己修改权限的试题在不具备编辑权限,自己又想编辑的时候,可以导入过来,进行编辑。

权限编辑功能,使得可以随时将不可导入的试题分享出来给别人。

在权限控制下,所有能够的修改都可以直接修改,修改引起的其他试卷修改,由用户自己负责。

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,让我们开始设计这个应用程序。首先,我们需要导入必要的模块和库: ```python import tkinter as tk from tkinter import messagebox import sqlite3 ``` 然后,我们可以定义一个函数来创建和连接到数据库: ```python def create_database(): conn = sqlite3.connect('survey.db') c = conn.cursor() c.execute('''CREATE TABLE IF NOT EXISTS survey_results (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, age INTEGER NOT NULL, gender TEXT NOT NULL, feedback TEXT NOT NULL)''') conn.commit() conn.close() ``` 此函数将创建一个名为“survey.db”的数据库,并在其中创建一个名为“survey_results”的表。此表将包含以下列:id、name、age、gender和feedback。 接下来,我们可以定义一个函数来验证用户登录: ```python def validate_login(): username = username_entry.get() password = password_entry.get() if username == 'admin' and password == 'admin123': login_window.destroy() db_window() else: messagebox.showerror("Error", "Invalid username or password") ``` 此函数将获取用户名和密码输入,并将其与预定义的用户名和密码进行比较。如果验证成功,则将关闭登录窗口并打开数据库窗口。否则,将显示错误消息框。 现在让我们设计登录窗口: ```python def login_window(): global login_window, username_entry, password_entry login_window = tk.Toplevel() login_window.title("Login") tk.Label(login_window, text="Username").grid(row=0, column=0) username_entry = tk.Entry(login_window) username_entry.grid(row=0, column=1) tk.Label(login_window, text="Password").grid(row=1, column=0) password_entry = tk.Entry(login_window, show="*") password_entry.grid(row=1, column=1) tk.Button(login_window, text="Login", command=validate_login).grid(row=2, column=0, columnspan=2) ``` 此函数将创建一个新的窗口,并将其标题设置为“Login”。它将在窗口中添加用户名和密码的标签和输入框,以及一个登录按钮。当用户单击登录按钮时,它将调用validate_login()函数。 接下来,让我们设计数据库窗口: ```python def db_window(): global db_window db_window = tk.Toplevel() db_window.title("Database") tk.Button(db_window, text="Survey", command=survey_window).pack() tk.Button(db_window, text="View Data", command=view_data).pack() ``` 此函数将创建一个新的窗口,并将其标题设置为“Database”。它将添加两个按钮:一个用于打开调查问卷窗口,另一个用于打开查询数据窗口。 接下来,我们可以设计调查问卷窗口: ```python def save_data(): name = name_entry.get() age = age_entry.get() gender = gender_var.get() feedback = feedback_entry.get('1.0', tk.END) conn = sqlite3.connect('survey.db') c = conn.cursor() c.execute("INSERT INTO survey_results (name, age, gender, feedback) VALUES (?, ?, ?, ?)", (name, age, gender, feedback)) conn.commit() conn.close() messagebox.showinfo("Success", "Data saved successfully") def survey_window(): global survey_window, name_entry, age_entry, gender_var, feedback_entry survey_window = tk.Toplevel() survey_window.title("Survey") tk.Label(survey_window, text="Name").grid(row=0, column=0) name_entry = tk.Entry(survey_window) name_entry.grid(row=0, column=1) tk.Label(survey_window, text="Age").grid(row=1, column=0) age_entry = tk.Entry(survey_window) age_entry.grid(row=1, column=1) tk.Label(survey_window, text="Gender").grid(row=2, column=0) gender_var = tk.StringVar(value="Male") tk.Radiobutton(survey_window, text="Male", variable=gender_var, value="Male").grid(row=2, column=1) tk.Radiobutton(survey_window, text="Female", variable=gender_var, value="Female").grid(row=2, column=2) tk.Label(survey_window, text="Feedback").grid(row=3, column=0) feedback_entry = tk.Text(survey_window, height=10, width=30) feedback_entry.grid(row=3, column=1, columnspan=2) tk.Button(survey_window, text="Save Data", command=save_data).grid(row=4, column=0, columnspan=3) ``` 此函数将创建一个新的窗口,并将其标题设置为“Survey”。它将包含一些文本标签和输入框,以便用户可以输入其调查问卷答案。它还将包含一个“Save Data”按钮,当用户单击该按钮时,它将调用save_data()函数将调查数据保存到数据库中。 最后,我们可以设计用于查询数据的窗口: ```python def view_data(): conn = sqlite3.connect('survey.db') c = conn.cursor() c.execute("SELECT * FROM survey_results") results = c.fetchall() conn.close() view_window = tk.Toplevel() view_window.title("View Data") scrollbar = tk.Scrollbar(view_window) scrollbar.pack(side=tk.RIGHT, fill=tk.Y) data_listbox = tk.Listbox(view_window, yscrollcommand=scrollbar.set) data_listbox.pack(fill=tk.BOTH, expand=1) for result in results: data_listbox.insert(tk.END, result) scrollbar.config(command=data_listbox.yview) ``` 此函数将连接到数据库并检索所有调查数据。它将创建一个新的窗口,并将其标题设置为“View Data”。它将包含一个列表框,其中包含从数据库中检索的所有数据。用户可以使用滚动条滚动列表。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值