咱们再往 SQL 的兔子洞里钻一钻:聊聊怎么在 FROM 结构里用子查询。这是 SQL 开发者最常用的套路之一,因为你可以直接“现场”造出很强的临时表,还能像数据库里真有这表一样反复用。
比如你要写个报表,需要算点东西、分组、过滤数据,但又不想在服务器上建一堆临时表。咋办?这时候 FROM 里的子查询就能救你。它们可以:
- 临时把数据合并或聚合一下,给主查询用。
- “现场”造结构化的数据集。
- 减少操作次数,让数据库不用存太多中间数据。
FROM 里的子查询就像迷你表,你可以在主查询里随便用。就像搭乐高一样:快、灵活,还不浪费资源 :)
FROM 里子查询的基础
在 FROM 里的子查询,就是用子查询造个临时表(或子表),让它变成整个查询的一部分。你只要做三步:
- 在
FROM结构里写个括号包起来的子查询。 - 给子查询起个别名(alias)。
- 像用普通表一样用这个别名。
语法
SELECT 列
FROM (
SELECT 列
FROM 表
WHERE 条件
) AS 别名
WHERE 外部_条件;
是不是有点吓人?咱们直接看例子吧。
例子:学生和平均分
假设我们有两张表:
students(学生信息——名字和 ID):
| student_id | student_name |
|---|---|
| 1 | Alex |
| 2 | Anna |
| 3 | Dan |
grades(学生成绩):
| grade_id | student_id | grade |
|---|---|---|
| 1 | 1 | 80 |
| 2 | 1 | 85 |
| 3 | 2 | 90 |
| 4 | 3 | 70 |
| 5 | 3 | 75 |
现在任务是:查出每个学生和他们的平均分。
我们可以先写个子查询算出每个学生的平均分,再在主查询里用它。
SELECT s.student_name, g.avg_grade
FROM (
SELECT student_id, AVG(grade) AS avg_grade
FROM grades
GROUP BY student_id
) AS g
JOIN students AS s ON s.student_id = g.student_id;
结果:
| student_name | avg_grade |
|---|---|
| Alex | 82.5 |
| Anna | 90.0 |
| Dan | 72.5 |
“现场”临时表
当你需要多层数据处理时,FROM 里的子查询特别有用。比如你不只想查平均分,还想查每个学生的最高分——全都在一个查询里搞定。
SELECT g.student_id, g.avg_grade, g.max_grade
FROM (
SELECT student_id,
AVG(grade) AS avg_grade,
MAX(grade) AS max_grade
FROM grades
GROUP BY student_id
) AS g;
结果:
| student_id | avg_grade | max_grade |
|---|---|---|
| 1 | 82.5 | 85 |
| 2 | 90 | 90 |
| 3 | 72.5 | 75 |
注意,这就像一个完整的临时表,有自己的列:avg_grade 和 max_grade。
什么时候用 FROM 里的子查询最合适?
聚合数据时。 如果你想先算一波(比如平均、总和、最大值),再和别的表合并。
过滤数据时。 需要在和主表合并前先过滤一遍数据。
简化复杂查询时。 把复杂任务拆成几步,避免写得太乱。
例子:两层处理的学生报表
假如我们想找出平均分大于 80 的学生。先用子查询算平均分,再在外层过滤。
SELECT s.student_name, g.avg_grade
FROM students AS s
JOIN (
SELECT student_id, AVG(grade) AS avg_grade
FROM grades
GROUP BY student_id
) AS g ON s.student_id = g.student_id
WHERE g.avg_grade > 80;
结果:
| student_name | avg_grade |
|---|---|
| Alex | 82.5 |
| Anna | 90.0 |
使用小贴士和建议
别名必不可少。 子查询一定要起 alias(比如 AS g),不然 PostgreSQL 不知道怎么用这个“临时表”。
优化。 FROM 里的子查询有时候比表 JOIN 慢,尤其是你在子查询里过滤数据时。
索引。 用来关联的字段、索引和过滤条件要优化好——对性能影响很大。
复杂查询例子:课程和学生数量
来点难度的。假设我们有这样一张表:
courses(课程列表):
| course_id | course_name |
|---|---|
| 1 | SQL Basics |
| 2 | Python Basics |
还有 enrollments(学生选课记录):
| student_id | course_id |
|---|---|
| 1 | 1 |
| 1 | 2 |
| 2 | 1 |
现在我们想知道每门课有多少学生报名。
SELECT c.course_name, e.students_count
FROM courses AS c
JOIN (
SELECT course_id, COUNT(student_id) AS students_count
FROM enrollments
GROUP BY course_id
) AS e ON c.course_id = e.course_id;
结果:
| course_name | students_count |
|---|---|
| SQL Basics | 2 |
| Python Basics | 1 |
希望你喜欢这节讲座...下一节更有意思 :)
GO TO FULL VERSION