
前言上一篇《数据库篇 13》中我们已经掌握了MySQL六大约束的使用学会了设计规范的单表结构保证了单表数据的完整性和一致性。本篇作为数据库篇的第十四篇我们将学习实际项目中最核心的数据库设计知识——多表关系。真实的业务系统中数据之间存在着复杂的关联不可能都存储在一张表中我们需要根据业务关系将数据拆分到多张表中并建立正确的关联关系。同时我们还会学习多表查询的基础——笛卡尔积理解多表查询的底层原理。本文为Python全栈开发者与数据库入门者量身打造详细讲解一对多、多对多、一对一三种最常见的多表关系每一种关系都有清晰的业务案例、设计原则和可直接运行的SQL代码同时深入解析笛卡尔积的概念和产生原因教你如何消除无效的笛卡尔积即使是完全没有数据库设计经验的同学也能快速掌握多表设计的核心方法。本节核心学习内容多表关系概述为什么需要多表设计与三种核心关系一对多关系最常用的关系类型与实现方式多对多关系中间表的设计原则与实现一对一关系单表拆分的最佳实践多表查询概述从多张表中查询数据的基本概念笛卡尔积底层原理、产生原因与消除方法完整实战三种关系的表结构创建与关联查询常见误区多表设计的常见错误与最佳实践核心总结多表关系速查表方便开发时快速查阅文章目录前言一、多表关系概述1.1 为什么需要多表设计1.2 多表关系的三种类型二、一对多多对一关系2.1 关系说明2.2 表结构设计与实现三、多对多关系3.1 关系说明3.2 表结构设计与实现四、一对一关系4.1 关系说明4.2 表结构设计与实现五、多表查询概述与笛卡尔积5.1 多表查询概述5.2 笛卡尔积概念问题演示消除无效的笛卡尔积六、完整实战演示6.1 需求说明6.2 实现代码七、常见误区与避坑指南八、核心总结多表关系速查表九、专栏订阅一、多表关系概述1.1 为什么需要多表设计如果将所有业务数据都存储在一张表中会出现以下严重问题数据冗余大量重复的数据会占用不必要的存储空间数据不一致修改数据时需要修改多处容易出现不一致的情况扩展性差新增业务字段时需要修改表结构影响所有数据维护困难表结构过于复杂难以理解和维护因此在进行数据库设计时我们需要根据业务模块之间的关系将数据拆分到多张表中每张表只负责存储一类数据然后通过外键建立表之间的关联关系。1.2 多表关系的三种类型在关系型数据库中表与表之间的关系基本上分为三种一对多多对一最常见的关系类型例如部门与员工、班级与学生多对多例如学生与课程、用户与角色一对一相对少见多用于单表拆分例如用户与用户详情二、一对多多对一关系一对多是最常用的多表关系也是所有多表关系的基础。2.1 关系说明案例部门与员工的关系关系描述一个部门可以对应多个员工一个员工只能对应一个部门实现方式在多的一方建立外键指向一的一方的主键2.2 表结构设计与实现-- 创建部门表一的一方CREATETABLEdept(idINTPRIMARYKEYAUTO_INCREMENTCOMMENT部门ID,nameVARCHAR(20)NOTNULLUNIQUECOMMENT部门名称)COMMENT部门表;-- 创建员工表多的一方CREATETABLEemp(idINTPRIMARYKEYAUTO_INCREMENTCOMMENT员工ID,nameVARCHAR(20)NOTNULLCOMMENT员工姓名,ageINTCOMMENT年龄,dept_idINTCOMMENT部门ID,-- 建立外键约束关联部门表的主键CONSTRAINTfk_emp_deptFOREIGNKEY(dept_id)REFERENCESdept(id))COMMENT员工表;-- 插入测试数据INSERTINTOdept(name)VALUES(研发部),(市场部),(财务部);INSERTINTOemp(name,age,dept_id)VALUES(张无忌,20,1),(杨逍,33,1),(赵敏,18,2),(周芷若,22,2),(张三丰,100,3);三、多对多关系多对多关系需要通过第三张中间表来实现中间表至少包含两个外键分别关联两张主表的主键。3.1 关系说明案例学生与课程的关系关系描述一个学生可以选修多门课程一门课程也可以供多个学生选择实现方式建立第三张中间表中间表至少包含两个外键分别关联两方主键3.2 表结构设计与实现-- 创建学生表CREATETABLEstudent(idINTPRIMARYKEYAUTO_INCREMENTCOMMENT学生ID,nameVARCHAR(20)NOTNULLCOMMENT学生姓名,noVARCHAR(20)NOTNULLUNIQUECOMMENT学号)COMMENT学生表;-- 创建课程表CREATETABLEcourse(idINTPRIMARYKEYAUTO_INCREMENTCOMMENT课程ID,nameVARCHAR(20)NOTNULLUNIQUECOMMENT课程名称)COMMENT课程表;-- 创建学生课程关系表中间表CREATETABLEstudent_course(idINTPRIMARYKEYAUTO_INCREMENTCOMMENT主键ID,studentidINTNOTNULLCOMMENT学生ID,courseidINTNOTNULLCOMMENT课程ID,-- 关联学生表CONSTRAINTfk_student_course_studentFOREIGNKEY(studentid)REFERENCESstudent(id),-- 关联课程表CONSTRAINTfk_student_course_courseFOREIGNKEY(courseid)REFERENCEScourse(id),-- 保证学生和课程的组合唯一避免重复选课UNIQUE(studentid,courseid))COMMENT学生课程关系表;-- 插入测试数据INSERTINTOstudent(name,no)VALUES(黛绮丝,2000100101),(谢逊,2000100102),(殷天正,2000100103),(韦一笑,2000100104);INSERTINTOcourse(name)VALUES(Java),(PHP),(MySQL),(Hadoop);INSERTINTOstudent_course(studentid,courseid)VALUES(1,1),(1,2),(1,3),(2,1),(2,4);四、一对一关系一对一关系多用于单表拆分将一张表的基础字段放在一张表中其他详情字段放在另一张表中以提升操作效率。4.1 关系说明案例用户与用户详情的关系关系描述一个用户只能对应一条用户详情记录一条用户详情记录也只能对应一个用户实现方式在任意一方加入外键关联另外一方的主键并且设置外键为唯一的(UNIQUE)4.2 表结构设计与实现-- 创建用户基本信息表CREATETABLEtb_user(idINTPRIMARYKEYAUTO_INCREMENTCOMMENT用户ID,nameVARCHAR(20)NOTNULLCOMMENT姓名,ageINTCOMMENT年龄,genderCHAR(1)COMMENT性别,phoneVARCHAR(11)COMMENT手机号)COMMENT用户基本信息表;-- 创建用户教育信息表详情表CREATETABLEtb_user_edu(idINTPRIMARYKEYAUTO_INCREMENTCOMMENT主键ID,degreeVARCHAR(20)COMMENT学历,majorVARCHAR(20)COMMENT专业,primaryschoolVARCHAR(50)COMMENT小学,middleschoolVARCHAR(50)COMMENT中学,universityVARCHAR(50)COMMENT大学,useridINTUNIQUENOTNULLCOMMENT用户ID,-- 关联用户表CONSTRAINTfk_user_edu_userFOREIGNKEY(userid)REFERENCEStb_user(id))COMMENT用户教育信息表;-- 插入测试数据INSERTINTOtb_user(name,age,gender,phone)VALUES(黄渤,45,1,18800001111),(冰冰,35,2,18800002222),(马云,55,1,18800008888),(李彦宏,50,1,18800009999);INSERTINTOtb_user_edu(degree,major,primaryschool,middleschool,university,userid)VALUES(本科,舞蹈,静安区第一小学,静安区第一中学,北京舞蹈学院,1),(硕士,表演,朝阳区第一小学,朝阳区第一中学,北京电影学院,2),(本科,英语,杭州市第一小学,杭州市第一中学,杭州师范大学,3),(本科,应用数学,阳泉第一小学,阳泉区第一中学,清华大学,4);五、多表查询概述与笛卡尔积5.1 多表查询概述多表查询就是指从多张表中查询数据。当我们需要查询的数据分布在多张表中时就需要使用多表查询。例如查询员工的姓名及其所在的部门名称员工姓名在员工表中部门名称在部门表中就需要同时查询这两张表。5.2 笛卡尔积概念笛卡尔乘积是指在数学中两个集合A和B的所有组合情况。在多表查询时如果没有指定任何关联条件就会产生笛卡尔积返回两张表所有记录的组合。问题演示-- 查询员工表和部门表不指定任何关联条件SELECT*FROMemp,dept;执行结果会返回3个部门 × 5个员工 15条记录但其中大部分都是无效的组合例如张无忌属于市场部、赵敏属于研发部等这些都是不符合实际业务逻辑的。消除无效的笛卡尔积在多表查询时必须添加关联条件只保留符合关联条件的有效记录-- 添加关联条件查询员工姓名及其所在部门名称SELECTemp.name,dept.nameFROMemp,deptWHEREemp.dept_iddept.id;执行结果会返回5条有效记录每个员工都对应了正确的部门名称。六、完整实战演示下面我们通过一个综合案例演示三种多表关系的联合使用和简单的多表查询。6.1 需求说明查询所有员工的姓名、年龄及其所在的部门名称查询所有学生的姓名、学号及其选修的课程名称查询所有用户的姓名、手机号及其学历信息6.2 实现代码-- 1. 查询员工及其部门信息SELECTe.nameAS员工姓名,e.ageAS年龄,d.nameAS部门名称FROMemp e,dept dWHEREe.dept_idd.id;-- 2. 查询学生及其选修的课程信息SELECTs.nameAS学生姓名,s.noAS学号,c.nameAS课程名称FROMstudent s,course c,student_course scWHEREs.idsc.studentidANDc.idsc.courseid;-- 3. 查询用户及其教育信息SELECTu.nameAS用户名,u.phoneAS手机号,edu.degreeAS学历FROMtb_user u,tb_user_edu eduWHEREu.idedu.userid;七、常见误区与避坑指南不要用单表存储所有数据单表存储会导致严重的数据冗余和不一致问题一定要根据业务关系进行合理的表拆分。外键命名规范外键建议使用fk_子表名_父表名的命名方式例如fk_emp_dept便于识别和维护。中间表的设计原则多对多关系的中间表除了两个外键外不要添加其他业务字段中间表只负责建立关联关系。一对一关系的使用场景只有当单表字段过多且大部分查询只需要访问部分字段时才考虑使用一对一关系进行表拆分。笛卡尔积的危害多表查询时一定要添加关联条件否则会产生大量无效数据严重影响查询性能甚至导致数据库崩溃。表别名的使用多表查询时建议给表起别名简化SQL语句避免字段名冲突。八、核心总结多表关系速查表为了方便后续开发时快速查阅整理了三种多表关系的核心速查表关系类型业务案例实现方式核心要点一对多部门-员工、班级-学生在多的一方建立外键指向一的一方的主键最常用的关系类型多对多学生-课程、用户-角色建立第三张中间表包含两个外键分别关联两方主键中间表必须有联合唯一约束一对一用户-用户详情在任意一方建立外键关联另一方主键并设置外键唯一多用于单表拆分提升查询效率笛卡尔积-添加关联条件消除多表查询必须添加关联条件否则会产生无效数据九、专栏订阅专栏优点《Python从入门到实战》专栏内容涵盖Python基础到高级编程、并发编程进程/线程/协程、网络编程TCP/UDP/Socket、核心内置/第三方模块、数据库核心实战、Web开发Django/Flask/FastAPI框架、数据库MySQL/ORM/异步数据库、网络爬虫同步/异步/分布式、AI实战、Linux部署运维等全栈核心知识以项目驱动教学构建清晰学习路径适合零基础入门和进阶提升的同学跟着一步步从入门到精通专栏地址https://blog.csdn.net/zsh_1314520/category_13108073.html文章是永久吗一次订阅后可永久免费查看专栏内所有文章后续会持续更新全栈相关内容第一时间获取最新教程有答疑交流群吗订阅专栏后有专属的全栈学习答疑群群内提供专业问题答疑、和众多学习者抱团取暖一起沉淀技术、赋能成长进群方式订阅专栏后可直接在专栏内申请加入答疑群或私信博主沟通进群事宜https://bbs.csdn.net/topics/620104702更多干货点赞收藏关注博主不迷路博主博客链接https://blog.csdn.net/zsh_1314520?spm1000.2115.3001.5343专注Python全栈技术分享评论区留言问题会一一回复助力大家轻松搞定Python全栈【原创声明】除本文原文地址以外如发现同款内容皆为盗版本文已收录于《Python全栈从入门到实战》请勿购买盗版文章和专栏如购买盗版内容不提供任何服务。原文地址https://blog.csdn.net/zsh_1314520/article/details/163112457