想象一下你在大学当数据分析师(我们不是在做大学数据库嘛,还记得吧?)。有人让你不只是查学生和他们的分数,还要在表里加一列显示小组里的最高分,这样就能方便对比成绩了。怎么搞?当然是用 SELECT 里的子查询啦!
SELECT 里的子查询可以让你在主查询执行的时候直接算出值。很酷,因为你可以把聚合计算、复杂过滤,甚至别的数据集合都合在一个查询里。
SELECT 里嵌套查询的基础
在 SELECT 里的子查询,字面意思就是:你把一个 SELECT 的结果塞进另一个 SELECT 里。这样就能给每一行结果算出额外的值。
来看个简单例子。假设我们有个 students 表,结构如下:
| student_id | name | group_id |
|---|---|---|
| 1 | Linda | 101 |
| 2 | Otto | 102 |
| 3 | Anna | 101 |
还有个 grades 表:
| grade_id | student_id | grade |
|---|---|---|
| 1 | 1 | 5 |
| 2 | 1 | 4 |
| 3 | 2 | 3 |
| 4 | 3 | 5 |
| 5 | 3 | 4 |
例子1:加上小组最高分
需求:查出学生名字、他们的分数,还有小组里的最高分,这样就能看到每个学生和小组最好成绩的差距。
SQL代码:
SELECT
s.name AS student_name,
g.grade AS student_grade,
(
SELECT MAX(grade) -- 这个查询只返回一个值
FROM grades
INNER JOIN students ON grades.student_id = students.student_id
WHERE students.group_id = s.group_id
) AS max_group_grade
FROM
students s
INNER JOIN
grades g ON s.student_id = g.student_id;
这里发生了什么:
- 对每个学生,我们查出他的名字和分数(
s.name,g.grade)。 SELECT MAX(grade)—— 这是个子查询,返回该学生小组里的最高分。- 子查询对主查询的每一行都会执行一次,用
WHERE students.group_id = s.group_id限定只查一个小组。
例子2:小组平均分
想让分析师更爽?我们不只查最高分,还查小组的平均分。
SQL代码:
SELECT
s.name AS student_name,
g.grade AS student_grade,
(
SELECT AVG(grade)
FROM grades
INNER JOIN students ON grades.student_id = students.student_id
WHERE students.group_id = s.group_id
) AS avg_group_grade
FROM
students s
INNER JOIN
grades g ON s.student_id = g.student_id;
现在:
- 我们用
AVG()替换了MAX(),来算小组的平均分。 - 这样就能“实时”分析数据啦。
限制和建议
SELECT 里的子查询很强大,但用的时候要注意:
- 性能。 每一行主查询都会执行一次子查询。如果表很大,SQL 查询会变慢。比如有1000个学生,子查询就会跑1000次!
- 索引。 为了让这种查询快点,最好给子查询
WHERE里用到的列加索引。 - 可读性。 别嵌套太多层。如果子查询太复杂,考虑放到
FROM里或者用临时表。
使用场景举例
再来看几个有意思的例子。
例子3:每个学生的课程数量
查出每个学生选了多少门课。enrollments 表通过 student_id 跟学生表关联:
| student_id | course_id |
|---|---|
| 1 | 201 |
| 1 | 202 |
| 2 | 201 |
| 3 | 203 |
SQL代码:
SELECT
s.name AS student_name,
(
SELECT COUNT(*)
FROM enrollments
WHERE enrollments.student_id = s.student_id
) AS course_count
FROM
students s;
这里子查询就是统计 enrollments 表里每个学生的记录数。
例子4:每个学生的“优等生”标记
来看看学生是不是优等生。标准是:他所有分数都是5。
SQL代码:
SELECT
s.name AS student_name,
(
SELECT CASE
WHEN MIN(g.grade) = 5 THEN '优等生'
ELSE '不是优等生'
END
FROM grades g
WHERE g.student_id = s.student_id
) AS status
FROM
students s;
这里用了嵌套 CASE,只有当 所有 分数都是5,才给“优等生”状态。
SELECT 里子查询的优化
我们说过,性能可能会有问题。这里有几个小建议:
- 用索引。 如果子查询有过滤条件,记得给相关列加索引。
- 缓存结果。 有时候可以把子查询放到
VIEW或临时表里。 - 少嵌套。 能简单就别搞太多层嵌套。
SELECT 里的子查询让你能做很多计算和数据分析。虽然有时候会吃资源,但只要优化好,SQL 就能变得更灵活更强大。大胆试试,找到属于你的查询优化方法吧!
GO TO FULL VERSION