数据库多表查询全攻略

🔗 数据库多表查询全攻略:告别笛卡尔积,玩转各种 JOIN 与高级嵌套!

引言:数据孤岛?不存在的!

在真实的业务世界里,数据从来都不是孤立存在的。用户信息存在 user 表,订单信息存在 order 表,商品详情又在 product 表......如果只会单表查询,你就只能看到一堆零散的碎片。

这时候,就需要**多表查询(Multi-table Query)**登场了!它就像一位高超的"红娘",能把分散在不同表里的数据完美地牵线搭桥,组合成我们想要的完整视图。今天,我们就来彻底搞懂多表查询的各种姿势,让你在面对复杂业务时游刃有余!

有关单表查询可以看这篇!!!


🕸️ 第一章:多表关系的"前世今生"

在写 SQL 之前,先要搞清楚表与表之间是什么关系。通常只有以下三种:

  1. 一对多(1:N) :最常见。比如一个部门有多个员工,但一个员工只能属于一个部门。实现方式:在"多"的一方(员工表)建立外键,指向"一"的一方(部门表)。

  2. 多对多(N:M) :比如一个学生可以选多门课,一门课也能被多个学生选。实现方式:必须建立一个中间表(如选课表),里面至少包含两个外键,分别关联两张主表。

  3. 一对一(1:1) :比较少见,通常是为了拆分大表优化性能。比如用户基础信息和用户隐私详情。实现方式:在任意一方加外键并设置唯一约束(UNIQUE)。


💥 第二章:连接查询------JOIN 的四大门派

多表查询的核心就是"连接"。如果不加任何条件直接把两张表拼在一起,会产生恐怖的笛卡尔积 (A表100行,B表100行,结果变成了10000行!)。为了避免这种情况,我们需要用 JOIN 来精准匹配。

1. 内连接(INNER JOIN):只要"两情相悦"

内连接只返回两张表中完全匹配的数据。就像找对象,必须双方都看对眼了才行。

sql 复制代码
-- 查询有部门的员工姓名和部门名称
SELECT 
    e.name, 
    d.dept_name 
FROM emp e 
INNER JOIN dept d ON e.dept_id = d.id;

💡 提示INNER 可以省略,直接写 JOIN 默认就是内连接。

2. 外连接(OUTER JOIN):总有一方是"备胎"

外连接会保证某一张表的数据全部显示出来,另一张表没匹配上的就填 NULL。

  • 左外连接(LEFT JOIN) :以左表 为主。左表数据全都要,右表匹配不上拉倒。场景 :查询所有员工及其部门信息(哪怕有些新员工还没分配部门,也要把人名查出来)。

    sql 复制代码
    SELECT 
        e.name, 
        d.dept_name 
    FROM emp e 
    LEFT JOIN dept d ON e.dept_id = d.id;
  • 右外连接(RIGHT JOIN) :以右表 为主。逻辑和左连接相反,但在实际开发中,大家习惯统一用 LEFT JOIN,把想保全的表放在左边就行。

3. 自连接(SELF JOIN):自己和自己谈恋爱?

当一张表里既有数据又有层级关系时(比如员工表里存了 manager_id 领导编号),就需要把这张表当成两张表来用。场景:查询每个员工的上级领导名字。

sql 复制代码
-- 把 emp 表看成两份:a代表员工,b代表领导
SELECT 
    a.name AS '员工', 
    b.name AS '领导' 
FROM emp a 
LEFT JOIN emp b ON a.manager_id = b.id;

⚠️ 注意:自连接必须给表起别名(如 a 和 b),否则数据库会晕头转向。


🪆 第三章:子查询------SQL 里的"套娃"艺术

如果不想用 JOIN,或者逻辑太复杂,我们还可以在 SQL 语句里再嵌套一个 SELECT 语句,这就是子查询

根据子查询返回的结果不同,可以分为以下几类:

1. 标量子查询(返回单个值)

子查询的结果只有一个数,通常配合 =, >, < 使用。场景:查询工资高于公司平均工资的员工。

sql 复制代码
SELECT * 
FROM emp 
WHERE salary > (
    SELECT AVG(salary) 
    FROM emp
);
2. 列子查询(返回一列数据)

子查询返回一列多行,通常配合 IN, ANY, ALL 使用。场景:查询在"销售部"或"市场部"工作的所有员工。

sql 复制代码
SELECT * 
FROM emp 
WHERE dept_id IN (
    SELECT id 
    FROM dept 
    WHERE name IN ('销售部', '市场部')
);
3. 进阶玩法:带 ANY 和 ALL 的子查询

当子查询返回多个值时,我们可以用这两个关键字进行更精细的比较:

  • ANY(任意一个) :只要满足子查询结果中的任意一个值即可。场景 :查询比"研发部"任意一个 员工工资高的其他部门员工(即只要比研发部最低工资高就行)。

    sql 复制代码
    SELECT name, salary 
    FROM emp 
    WHERE salary > ANY (
        SELECT salary 
        FROM emp 
        WHERE dept_id = (SELECT id FROM dept WHERE name = '研发部')
    ) AND dept_id != (SELECT id FROM dept WHERE name = '研发部');
  • ALL(所有) :必须满足子查询结果中的所有值。场景 :查询比其他部门所有 员工工资都高的员工(即比所有非本部门员工的最高工资还要高)。

    sql 复制代码
    SELECT name, salary 
    FROM emp 
    WHERE salary > ALL (
        SELECT salary 
        FROM emp 
        WHERE dept_id = 30
    );
4. 降维打击:带 EXISTS 的子查询

EXISTS 不关心子查询的具体内容,只判断子查询是否有结果返回 (有结果则为真,无结果则为假)。它的执行效率通常在大数据量下优于 IN场景:查询所有选修了 1 号课程的学生姓名(相关子查询)。

sql 复制代码
SELECT name 
FROM student s 
WHERE EXISTS (
    SELECT 1 
    FROM score sc 
    WHERE sc.sno = s.sno AND sc.cno = '1'
);

💡 技巧 :子查询里写 SELECT 1SELECT * 都可以,因为数据库只在乎"有没有",不在乎"是什么"。与之对应的还有 NOT EXISTS,用来查找"不存在"的记录。

5. 派生表(放在 FROM 后面)

把子查询的结果当成一张临时表来用。场景:统计各部门的平均薪资,并筛选出平均薪资大于 10000 的部门详情。

sql 复制代码
SELECT 
    d.*, 
    temp.avg_sal 
FROM dept d 
JOIN (
    SELECT dept_id, AVG(salary) as avg_sal 
    FROM emp 
    GROUP BY dept_id
) temp ON d.id = temp.dept_id 
WHERE temp.avg_sal > 10000;

⚠️ 避坑 :放在 FROM 后面的子查询,一定要给它起个别名(如上面的 temp)!


⚖️ 第四章:集合查询------横向拼接的魔法

前面讲的 JOIN 都是横向拼接(增加字段),而集合查询是纵向拼接(增加行数)。它可以把两条或多条 SELECT 语句的结果合并起来。

  • UNION:合并后会自动去重,效率较低。

  • UNION ALL:直接合并,不去重,效率高(推荐使用)。

  • INTERSECT(交集):返回两个查询结果中共同拥有的部分(MySQL 8.0+ 支持,老版本可用 INNER JOIN 替代)。

  • EXCEPT / MINUS(差集):返回第一个查询有,但第二个查询没有的部分(Oracle 叫 MINUS,SQL Server 叫 EXCEPT,MySQL 可用 LEFT JOIN 替代)。

场景:要把中国城市和外国城市合并到一个列表展示。

sql 复制代码
SELECT name, country FROM cn_cities
UNION ALL
SELECT name, country FROM en_cities;

⚠️ 强制规范 :参与合并的查询语句,它们的字段数量和数据类型必须一致


📝 总结:多表查询速查宝典

为了让大家不再混淆,我把核心知识点整理成了表格:

查询方式 核心关键词 适用场景 注意事项
内连接 INNER JOIN 只要两边都有的数据 最常用的连接方式
左外连接 LEFT JOIN 保左表,右表没有补NULL 生产环境最常用,替代右连接
自连接 JOIN ... ON 同一张表内的层级关系 必须给表起不同的别名
子查询 ANY / ALL 与子查询结果集进行比较 ANY满足其一,ALL需满足全部
子查询 EXISTS 判断子查询是否有结果 大数据量下效率通常优于 IN
联合查询 UNION / ALL 合并结构相同的多张表 字段数量和类型必须对齐

掌握了这些多表查询的技巧,你就不再是那个对着单表发愁的新手了。下次遇到复杂的报表需求,试着拆解一下表之间的关系,灵活运用 JOIN、高级子查询和集合运算,你会发现 SQL 的世界原来如此广阔!

如果觉得这篇干货对你有用,别忘了点赞、在看、转发三连哦!我们下期见!👋

相关推荐
想唱rap3 小时前
IO多路转接之epoll
linux·运维·服务器·数据库·网络协议·算法·http
l1t3 小时前
DeepSeek总结的PostgreSQL 19 Beta 的四个特性
数据库·postgresql
山屿落星辰3 小时前
cann-learning-hub - 昇腾CANN学习资源一站式指南
学习
ZC跨境爬虫4 小时前
模块化烹饪小程序开发日记 Day3:(Flask后端初始化、数据库配置与自定义日志系统搭建)
前端·javascript·数据库·后端·python·flask
追梦开发者4 小时前
MongoDB 踩坑实录③:写操作、事务、聚合,踩一个就是线上事故
数据库·mongodb
星梦清河4 小时前
微服务-Redis高级
数据库·redis·缓存
想你依然心痛4 小时前
HarmonyOS 6(API 23)实战:基于悬浮导航、沉浸光感与HMAF的“鸿蒙代码导师“——PC端AI智能体沉浸式编程学习系统
人工智能·学习·harmonyos
zhangchengjava4 小时前
Redis 连接问题完整解决报告
数据库·redis·缓存
玄米乌龙茶1234 小时前
LLM 应用开发学习笔记:RAG 评估、参数调优与 Transformer 注意力机制
笔记·学习