跳转至

多表查询与高级查询操作

一、多表连接(JOIN)

在实际项目中,我们往往需要同时查询多张表的数据,比如学生信息和成绩信息。此时就需要使用 JOIN(连接查询)

类型 说明 示例
INNER JOIN 仅返回匹配记录 SELECT * FROM A INNER JOIN B ON A.id = B.aid;
LEFT JOIN 返回左表所有记录 SELECT * FROM A LEFT JOIN B ON A.id = B.aid;
RIGHT JOIN 返回右表所有记录 SELECT * FROM A RIGHT JOIN B ON A.id = B.aid;
FULL JOIN 返回左右表所有记录 SELECT * FROM A FULL JOIN B ON A.id = B.aid;

1.1 内连接(INNER JOIN)

语法:

SELECT A.列名, B.列名
FROM A AS A
INNER JOIN B AS B
ON A.公共列 = B.公共列;

示例:

SELECT s.name, c.course_name
FROM students s
INNER JOIN courses c
ON s.course_id = c.id;

💡 特点:只返回两表中匹配的记录。


1.2 左连接(LEFT JOIN)

语法:

SELECT A.*, B.*
FROM A
LEFT JOIN B
ON A.公共列 = B.公共列;

示例:

SELECT s.name, c.course_name
FROM students s
LEFT JOIN courses c
ON s.course_id = c.id;

💡 左表数据全部显示,即使右表无匹配值(右表对应列为 NULL)。


1.3 右连接(RIGHT JOIN)

SELECT s.name, c.course_name
FROM students s
RIGHT JOIN courses c
ON s.course_id = c.id;

💡 右表数据全部显示,即使左表无匹配值。


1.4 全连接(FULL JOIN)

SELECT s.name, c.course_name
FROM students s
FULL JOIN courses c
ON s.course_id = c.id;

💡 返回左表或右表中有匹配或未匹配的所有行。

⚠️ 注意:MySQL 不支持 FULL JOIN,可通过 UNION 模拟:

SELECT * FROM A LEFT JOIN B ON ...
UNION
SELECT * FROM A RIGHT JOIN B ON ...;

1.5 自连接(SELF JOIN)

一个表自己与自己连接,常用于表示层级关系。

示例:

SELECT e1.name AS 员工, e2.name AS 上级
FROM employees e1
LEFT JOIN employees e2
ON e1.manager_id = e2.id;

1.6 多表连接

可以同时连接三张及以上表:

SELECT s.name, c.course_name, t.teacher_name
FROM students s
JOIN courses c ON s.course_id = c.id
JOIN teachers t ON c.teacher_id = t.id;

1.7 不同数据库 JOIN 对比

数据库 支持的连接类型 特殊说明
MySQL INNER, LEFT, RIGHT 不支持 FULL JOIN,可用 UNION 模拟
SQL Server INNER, LEFT, RIGHT, FULL 支持全连接
PostgreSQL 全部支持 完整标准实现
Oracle INNER, LEFT, RIGHT, FULL 旧版使用 (+) 语法

二、子查询(Subquery)

定义:子查询是嵌套在另一条 SQL 语句中的查询结果,用于提供动态条件或中间结果集。


2.1 子查询类型

类型 返回结果 使用位置
标量子查询 单个值 WHERE、SELECT
列子查询 一列多行 IN、NOT IN
行子查询 多列 比较运算
表子查询 一张虚拟表 FROM
EXISTS 子查询 布尔结果 EXISTS、NOT EXISTS

2.2 标量子查询

返回单个值。

SELECT name, age
FROM students
WHERE score = (SELECT MAX(score) FROM students);

2.3 列子查询

SELECT name FROM students
WHERE id IN (SELECT student_id FROM scores WHERE course='Math');

2.4 行子查询

SELECT name FROM employees
WHERE (dept_id, salary) IN (SELECT dept_id, MAX(salary) FROM employees GROUP BY dept_id);

2.5 表子查询(FROM 子句中)

SELECT sub.dept_id, AVG(sub.salary)
FROM (
  SELECT dept_id, salary FROM employees WHERE salary > 3000
) AS sub
GROUP BY sub.dept_id;

2.6 EXISTS 与 NOT EXISTS

SELECT name FROM students s
WHERE EXISTS (SELECT 1 FROM scores sc WHERE sc.student_id = s.id);

💡 EXISTS 判断子查询结果是否存在,通常性能优于 IN。


2.7 子查询优化建议

  • 尽量用 JOIN 替代复杂子查询。
  • 对子查询中的过滤列建立索引。
  • 使用 EXISTS 替代 IN(在大数据量下更高效)。

三、集合操作(Set Operations)

集合操作用于将多个查询结果合并。要求列数与类型一致。


3.1 UNION(并集,去重)

SELECT name FROM students_2024
UNION
SELECT name FROM students_2025;

💡 自动去重。


3.2 UNION ALL(并集,不去重)

SELECT name FROM students_2024
UNION ALL
SELECT name FROM students_2025;

💡 性能更好,不去重。


3.3 INTERSECT(交集)

SELECT name FROM students_2024
INTERSECT
SELECT name FROM students_2025;

💡 MySQL 不支持,可用 INNER JOIN 模拟:

SELECT s1.name FROM students_2024 s1
INNER JOIN students_2025 s2 ON s1.name = s2.name;

3.4 EXCEPT / MINUS(差集)

-- PostgreSQL / SQL Server
SELECT name FROM students_2024
EXCEPT
SELECT name FROM students_2025;

-- Oracle
SELECT name FROM students_2024
MINUS
SELECT name FROM students_2025;

💡 返回仅在第一个查询中存在的数据。


3.5 集合操作规则

项目 要求
列数 必须相同
列类型 对应位置兼容
列名 取第一个 SELECT 的列名
ORDER BY 放在整个语句末尾

四、综合实例:多表 + 子查询 + 集合操作

-- 查询所有平均分高于全校平均分的学生姓名
SELECT s.name, AVG(sc.score) AS avg_score
FROM students s
JOIN scores sc ON s.id = sc.student_id
GROUP BY s.name
HAVING AVG(sc.score) > (SELECT AVG(score) FROM scores);
-- 合并不同年度选课学生(去重)
SELECT student_id FROM enrollment_2024
UNION
SELECT student_id FROM enrollment_2025;
-- 查询2024年选课但2025年未选课的学生
SELECT student_id FROM enrollment_2024
EXCEPT
SELECT student_id FROM enrollment_2025;

五、总结

分类 关键语法 说明
表连接 JOIN 多表间数据整合
子查询 SELECT 嵌套 动态条件查询
集合操作 UNION / INTERSECT / EXCEPT 合并、交集、差集
性能优化 索引 / EXISTS / JOIN 替代 提升执行效率