如何在一个表中存储多个选项?

时间:2015-09-16 22:36:32

标签: mysql database-design

我想设计一个结果计算应用程序。

首先,我需要知道如何在MySQL数据库中存储记录,以便学生可以将多少课程附加到他们身上,例如:学生A可以附加6个科目,而学生B可以附加12个科目。

在这种情况下,我需要知道如何设计一个数据库结构,允许字段以数组的形式存储尽可能多的主题。

任何建议或更好的方法来处理这一点都将受到高度赞赏。

2 个答案:

答案 0 :(得分:29)

请阅读Data NormalizationGeneral Indexing概念和Foreign Key约束,以便通过参照完整性保持数据清晰。这会让你前进。

将数据存储在数组中对您来说似乎很自然,但对于db引擎而言,性能主要是没有索引使用。此外,您将在第2天发现,获取和维护您的数据将是一场噩梦。

以下应该让你在修补时有一个良好的开端。 Joins也是。

create table student
(   studentId int auto_increment primary key,
    fullName varchar(100) not null
    -- etc
);

create table dept
(   deptId int auto_increment primary key,
    deptName varchar(100) not null -- Economics
    -- etc
);

create table course
(   courseId int auto_increment primary key,
    deptId int not null,
    courseName varchar(100) not null,
    -- etc
    CONSTRAINT fk_crs_dept FOREIGN KEY (deptId) REFERENCES dept(deptId)
);

create table SCJunction
(   -- Student/Course Junction table (a.k.a Student is taking the course)
    -- also holds the attendance and grade
    id int auto_increment primary key,
    studentId int not null,
    courseId int not null,
    term int not null, -- term (I am using 100 in below examples for this term)
    attendance int not null, -- whatever you want, 100=always there, 0=he must have been partying,
    grade int not null, -- just an idea   
    -- See (Note Composite Index) at bottom concerning next two lines.
    unique key(studentId,courseId,term), -- no duplicates allowed for the combo (note student can re-take it next term)
    key (courseId,studentId),
    CONSTRAINT fk_sc_student FOREIGN KEY (studentId) REFERENCES student(studentId),
    CONSTRAINT fk_sc_courses FOREIGN KEY (courseId) REFERENCES course(courseId)
);

创建测试数据

insert student(fullName) values ('Henry Carthage'),('Kim Billings'),('Shy Guy'); -- id's 1,2,3
insert student(fullName) values ('Shy Guy');

insert dept(deptName) values ('History'),('Math'),('English'); -- id's 1,2,3

insert course(deptId,courseName) values (1,'Early Roman Empire'),(1,'Italian Nation States'); -- id's 1 and 2 (History dept)
insert course(deptId,courseName) values (2,'Calculus 1'),(2,'Linear Algebra A'); -- id's 3 and 4 (Math dept)
insert course(deptId,courseName) values (3,'World of Chaucer'); -- id 5 (English dept)

-- show why FK constraints are important based on data at the moment
insert course(deptId,courseName) values (66,'Fly Fishing 101'); -- will generate error 1452. That dept 66 does not exist
-- That error is a good error to have. Better than faulty data

-- Have Kim (studentId=2) enrolled in a few courses
insert SCJunction(studentId,courseId,term,attendance,grade) values (2,1,100,-1,-1); -- Early Roman Empire, term 100 (made up), unknown attendance/grade
insert SCJunction(studentId,courseId,term,attendance,grade) values (2,4,100,-1,-1); -- Linear Algebra A
insert SCJunction(studentId,courseId,term,attendance,grade) values (2,5,100,-1,-1); -- World of Chaucer

-- Have Shy Guy (studentId=3) enrolled in one course only. He is shy
insert SCJunction(studentId,courseId,term,attendance,grade) values (3,5,100,-1,-1); -- Early Roman Empire, term 100 (made up), unknow attendance/grade
-- note if you run that line again, the Error 1062 Duplicate entry happens. Can't take same course more than once per term

一些简单的问题。

哪个部门的课程是什么?

show all,使用表别名(缩写)使输入更少,可读性(有时)更好

select c.courseId,c.courseName,d.deptId,d.deptName
from course c
join dept d
on c.deptId=d.deptId
order by d.deptName,c.courseName -- note the order
+----------+-----------------------+--------+----------+
| courseId | courseName            | deptId | deptName |
+----------+-----------------------+--------+----------+
|        5 | World of Chaucer      |      3 | English  |
|        1 | Early Roman Empire    |      1 | History  |
|        2 | Italian Nation States |      1 | History  |
|        3 | Calculus 1            |      2 | Math     |
|        4 | Linear Algebra A      |      2 | Math     |
+----------+-----------------------+--------+----------+

这个学期谁将参加乔的世界课程?

(知道courseId = 5)

以下是SCJunction中我们的复合索引之一。复合是多个列的索引。

select s.StudentId,s.FullName
from SCJunction j
join student s
on j.studentId=s.studentId
where j.courseId=5 and j.term=100
+-----------+--------------+
| StudentId | FullName     |
+-----------+--------------+
|         2 | Kim Billings |
|         3 | Shy Guy      |
+-----------+--------------+
<2> Kim Billings注册了这个词?
select s.StudentId,s.FullName,c.courseId,c.courseName
from SCJunction j
join student s
on j.studentId=s.studentId
join course c
on j.courseId=c.courseId
where s.studentId=2 and j.term=100
order by c.courseId DESC -- descending, just for the fun of it
+-----------+--------------+----------+--------------------+
| StudentId | FullName     | courseId | courseName         |
+-----------+--------------+----------+--------------------+
|         2 | Kim Billings |        5 | World of Chaucer   |
|         2 | Kim Billings |        4 | Linear Algebra A   |
|         2 | Kim Billings |        1 | Early Roman Empire |
+-----------+--------------+----------+--------------------+

Kim不知所措,所以放弃数学课

delete from SCJunction
where studentId=2 and courseId=4 and term=100

运行上面的选择语句,显示Kim正在采取的措施:

+-----------+--------------+----------+--------------------+
| StudentId | FullName     | courseId | courseName         |
+-----------+--------------+----------+--------------------+
|         2 | Kim Billings |        5 | World of Chaucer   |
|         2 | Kim Billings |        1 | Early Roman Empire |
+-----------+--------------+----------+--------------------+

啊,这个术语容易得多。爸爸不过不高兴。

请注意SCJunction.term之类的内容。关于这个问题可以写得很多,我现在大部分会跳过它,而不是说它应该在某个地方的FK中。您可能希望您的术语看起来更像SPRING2015而不是int。

就身份而言。这就是我这样做的方式。这是个人喜好。这需要知道id#&#39; s,查找它们。其他人可以选择一个类似HIST101而不是17的课程。那些可读性更高(但在索引中速度较慢(几乎没有)。所以最适合你的是。

注意综合指数

复合索引(INDEX表示KEY,反之亦然)是组合多列以进行快速数据检索的索引。 SCJunction表中的两个合成项的顺序将被翻转,这样,根据您的数据之后的查询范围,数据库引擎可以根据您要追踪的最左侧列选择要用于最快检索的索引。

对于唯一键#1,它旁边的注释声明强制执行没有重复(意味着垃圾数据)是相当不言自明的。例如,学生1课程1学期1在该表中不能存在两次。

理解的一个关键概念是索引中列名的left-most排序概念。

对于仅在studentId 之后的查询,将使用首先列出studentId的密钥(left-most)。在仅courseId 之后的查询中,将使用最左侧courseId的密钥。在追踪studentId和courseId的查询中,db引擎可以决定使用哪个复合键。

当我说&#34;去&#34;时,我指的是on clausewhere clause条件。

如果没有这两个复合键(其中第1列和第2列被翻转),那么在所查询的列不是left-most索引的查询中,您将不会受益于密钥使用,并且受到影响缓慢的表格扫描,以便返回数据。

因此,这两个索引结合了以下两个概念

  • 基于最左边或两者(studentId和courseId列)的快速数据检索
  • 根据studentId,courseId和术语值强制执行该表中的数据重复

The Takeaway

重要的外卖是,Junction表可以快速索引检索,并且数据的理智管理与逗号分隔的数据(数组思维模式)塞进一列,以及使用这些数据的所有痛苦构造

答案 1 :(得分:2)

为了完整起见,不是这是一般推荐的解决方案:

MySQL提供JSON datatype,它允许以JSON格式存储和检索对象和数组。

这样,您可以将整个对象和数组存储到一个字段中,因为数组看起来像:

 ['subject_1', 'subject_2', 'subject_3']

特别是初学者不知道这一点,他们通过另一个以逗号分隔的字符串实现或使用依赖于语言的序列化/反序列化方法来重新发明轮子。

至少JSON是非常常用的,并且很容易被解析为数据交换格式。

在MySQL字段中使用存储数组和对象有一些有效的用例,例如:用于速度优化,或者当您还有想要保存在数据库中的未知或动态属性时。

然而,根据经验,如果您依赖于将对象和数组存储到MySQL中,那么您的数据库设计很可能会被破坏。