【问题标题】:storing multiple values in one field在一个字段中存储多个值
【发布时间】:2011-09-23 03:59:07
【问题描述】:

我希望用户能够选择他们的专业。

例如,A 可以选择计算机科学、数学和历史作为他的专业。用户可以选择任意数量的专业。

我的数据库中有一个组织列表,这些组织只接受特定专业的学生。例如,组织 A 只接受计算机科学和数学专业的学生。组织可以选择任意数量的专业。

我想为学生匹配适合他们专业的组织。例如,我想在数据库中搜索接受一个或多个 Person 的 A 专业的组织,这些专业是计算机科学、数学和历史。接受全部或大部分 Person 的 A 专业的组织将被列在首位。因此,如果组织 B 接受人员 A 的所有三个专业,但组织 A 只接受人员 A 的两个专业,则组织 B 将首先列出。

如何将组织接受的专业存储在mysql数据库中?如何存储学生的专业,以实现学生信息和组织信息的高效匹配?

我正在考虑将组织接受的所有专业作为序列化值存储在数据库中。

所以我有两张桌子

组织
ID 整数
名称 varchar(255)
majors_accepted blob

学生
ID 整数
名称 varchar(255)
专业blob

我可以将组织接受的专业作为序列化值存储在majors_accepted blob 中。该领域可能有超过 1 个专业。

或者我可以将学生正在考虑的专业作为序列化值存储在学生表的专业字段中。该领域可能有超过 1 个专业。然后我想我可以遍历组织表中的所有行,并将每个专业的接受字段与学生数据进行比较。但这似乎效率低下……

【问题讨论】:

  • 您可以有另一个名为 Majors 的表,然后是 ObjectToMajors(或 StudentToMajors 和 OrganizationToMajors)的表,它将学生的 ID 链接到专业的 ID。如果您将两列(StudentId 和 MajorId)都设为键,那么您可以确保每个学生只能拥有每个专业中的一个(这是有道理的,不能成为一个专业的成员两次)。如果您使用 ObjectToMajor,则需要有一个字段来指定 ObjectId 是学生还是组织(这样您就知道如何关联数据)。

标签: php mysql sql database-design


【解决方案1】:

我会这样做:

  1. 创建一个专业表 - 有一个majorID 和一个majorName
  2. 在学生和专业之间创建一个关联表,其中包含一个学生 ID 和一个专业 ID。
  3. 在组织和专业之间创建具有 orgID 和 majorID 的关联表。

在一个字段中存储多个值(就像您尝试对 blob 执行的操作一样)不是一个好的数据库设计,所以像这样分解它,您可以执行所有必要的查询来确定学生是哪个组织只需加入表格即可加入。

假设您正在寻找某个特定学生可以加入的组织(我们会说该学生的 studentID 为 1):

SELECT Students.ID,
       Organizations.name
FROM Students 
INNER JOIN StudentsMajors ON Students.ID = StudentsMajors.studentID
INNER JOIN OrganizationsMajors ON StudentsMajors.majorID = OrganizationsMajors.majorID
INNER JOIN Oranizations ON OrganizationsMajors.orgID = Organizations.ID
WHERE Students.ID = 1

【讨论】:

  • 那么我将如何查询数据库以匹配学生与组织?例如,如果学生有以下专业:数学、历史、计算机科学。我将如何查询数据库以提供接受一个或多个专业的组织列表?我必须查询学生选择的每个专业的 organizations_majors 表吗?
【解决方案2】:

不要尝试将主要列表作为 blob 存储在单个列中,为此使用单独的关联表:

create table organization_majors (
    organization_id int not null,
    major_id        int not null,
    primary key (organization_id, major_id)
);
create table student_majors (
    student_id int not null,
    major_id   int not null,
    primary key (student_id, major_id)
);

您可能还想单独索引主键中的每一列,但索引取决于(通常)您可能使用的查询类型。

然后您可以使用标准 SQL 查询来检查专业是否匹配。例如,要查找 major_id 为 1 的所有学生:

select s.id, s.name
from students s join student_majors m on s.id = m.student_id
where m.major_id = 1

或查找可以在组织 11 中的所有学生:

select s.id, s.name
from students s
join student_majors sm on s.id = sm.student_id
join organization_majors om on sm.major_id = om.major_id
where om.organization_id = 11
group by s.id, s.name
having count(*) = (select count(*) from organization_majors where organization_id = 11)

【讨论】:

  • 我认为这对双主修学生不太适用。最好将专业分成单独的表格,例如@kinakuta 的答案
  • @Ted:这就是student_majors 的用途。还是我错过了什么?第二个示例查询确实假设一个组织仅在某人拥有该组织的所有专业但放宽这一点相当直接时才接受该人。
  • 你是对的。我不知道我以为您发布了什么,但显然这不是您实际发布的内容。
  • @Ted:酷,我很高兴我发布了我认为我发布的内容,我的手指并不总是在应该听我的大脑时(有时他们不会忽略我的大脑)应该)。
【解决方案3】:

学生和专业之间的关系是多对多的。正如其他响应者已经说过的,多对多的最佳设计是关联表。关联表将引用学生表和专业表,其中包含专业名称等数据。

在一个字段中存储多个值并使用逗号等分隔符是不好的设计。它违反了第一范式。当您违反第一范式时,您不能再对所有数据进行键控查找。

例如,您必须进行全表扫描才能找到具有给定专业的所有学生。这可能会导致数千个磁盘 ios,而不是少于一百个磁盘 ios 进行三路连接。那慢了十倍。

当您计划对关联表进行键控查找时,请注意良好的索引设计。一个好的查询优化器和好的索引设计可以从你的连接中获得最好的速度。幸运的是,您无需卸载和重新加载表即可返回并更改索引设计。

【讨论】:

    猜你喜欢
    • 2010-11-27
    • 2015-07-20
    • 2023-03-19
    • 1970-01-01
    • 2021-01-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-17
    相关资源
    最近更新 更多