当前位置:首页 > 问答 > 正文

数据库多对多关系怎么查,SQL联结那些事儿其实没那么难理解

综合自网络技术博客、社区问答及SQL入门教程的常见讲解思路)

说到数据库查询,最让人头大的可能就是那种“多对多”的关系了,别怕,咱们今天就用大白话把它捋清楚,你完全可以把它想象成现实生活中的情况,比如学生选课。

想象一个场景:一个学生,比如小明,可以选多门课,比如数学、语文、英语,反过来,一门课,比如数学,也可以被很多学生选,小红、小刚、小明都选了,这就是典型的多对多关系,在数据库里,我们怎么存这种关系呢?

直接在学生表里加一个“课程”字段?那不行,一个字段怎么存得下“数学、语文、英语”好几门课呢?同理,在课程表里加一个“学生”字段,也存不下“小红、小刚、小明”一堆人名。

聪明的数据库设计者想出了一个好办法:引入一个“中间人”,这个“中间人”通常叫做“联结表”或者“关联表”,在这个例子里,我们就可以创建一个叫“学生选课表”的表,这个表结构特别简单,通常就两列(可能再加一个主键ID),一列是“学生ID”,另一列是“课程ID”。

数据是这样存放的:

  • 学生表:放着所有学生的基本信息,比如学生ID、姓名。
  • 课程表:放着所有课程的基本信息,比如课程ID、课程名。
  • 学生选课表:它自己不存储具体的学生姓名或课程名,只记录“关系”,比如有一条记录是(学生ID: 1, 课程ID: 101),这就表示ID为1的学生(假设是小明)选了ID为101的课程(假设是数学)。

好了,数据存好了,现在轮到最关键的一步:怎么查? 这就需要用上SQL的“联结”查询了,而且这次需要联结两个地方。

数据库多对多关系怎么查,SQL联结那些事儿其实没那么难理解

我们的目标是:查詢出“小明”都选了哪些课程

这个查询需要分三步走,就像接力赛跑一样,需要两个“接力棒”:

第一步:从“学生表”跑到“学生选课表”。 我们得先知道“小明”在学生表里的ID是多少,假设我们查到他ID是1,我们就要去“学生选课表”里,找出所有“学生ID”等于1的记录,这样,我们就得到了小明选的所有课程的ID列表,我们找到了三条记录:(学生ID:1, 课程ID:101), (学生ID:1, 课程ID:102), (学生ID:1, 课程ID:103),现在我们知道小明选了ID为101、102、103的课程。

第二步:从“学生选课表”跑到“课程表”。 光有课程ID不行啊,我们想知道课程的名字,我们得拿着刚才得到的课程ID列表(101, 102, 103),去“课程表”里找对应ID的课程名称,课程ID 101对应“数学”,102对应“语文”,103对应“英语”。

数据库多对多关系怎么查,SQL联结那些事儿其实没那么难理解

第三步:把结果拼起来。 我们把“学生表”里小明的名字,和“课程表”里查到的课程名,组合成最终结果。

在SQL语言里,我们不需要真的分三步写三个查询语句,用一个语句就能搞定,这个语句里会用到两次“联结”(JOIN),具体的写法看起来大概是这样的:

SELECT 学生表.姓名, 课程表.课程名
FROM 学生表
JOIN 学生选课表 ON 学生表.ID = 学生选课表.学生ID
JOIN 课程表 ON 学生选课表.课程ID = 课程表.ID
WHERE 学生表.姓名 = '小明';

我们来拆解一下这个“天书”:

  • FROM 学生表:这是我们查询的起点。
  • JOIN 学生选课表 ON 学生表.ID = 学生选课表.学生ID:这就是第一步接力,意思是把“学生表”和“学生选课表”连接起来,连接的条件是两张表里的“学生ID”要相等,这样,数据库就能把“小明”和他选课的记录对应上了。
  • JOIN 课程表 ON 学生选课表.课程ID = 课程表.ID:这就是第二步接力,意思是把上一步的结果(已经包含了小明和他的课程ID)再和“课程表”连接起来,连接的条件是“学生选课表”里的课程ID和“课程表”里的ID相等,这样,数据库就能把课程ID转换成具体的课程名了。
  • SELECT 学生表.姓名, 课程表.课程名:我们从这一大串连接好的结果里,只选出我们关心的两列:学生的姓名和课程的名字。
  • WHERE 学生表.姓名 = '小明':这个好理解,我们只查小明的信息。

你看,整个过程就像是一个连环扣:学生表 -> (通过学生ID扣上) 选课表 -> (通过课程ID扣上) 课程表,通过这个“中间人”(选课表),我们就把多对多的关系,拆解成了两个一对多的关系来查询(一个学生对应多条选课记录,一门课程对应多条选课记录),问题就变得简单了。

反过来查询也是一样的道理,比如想查“数学”课都有哪些学生选了,只需要把查询的起点和条件换一下:

SELECT 课程表.课程名, 学生表.姓名
FROM 课程表
JOIN 学生选课表 ON 课程表.ID = 学生选课表.课程ID
JOIN 学生表 ON 学生选课表.学生ID = 学生表.ID
WHERE 课程表.课程名 = '数学';

核心思想永远不变:依靠中间表,进行两次联结,多对多关系查询的难点,一旦你理解了中间表的核心作用,并且把JOIN ON理解为“根据某个条件把两张表的数据拼接到一起”,就会发现它其实就这么回事儿,真的没那么难理解,下次再遇到,就在纸上画一下这三个表,标出它们是通过哪个字段连接的,思路瞬间就清晰了。