CodeGym /课程 /SQL SELF /SELECT 里面用 SELECT

SELECT 里面用 SELECT

SQL SELF
第 13 级 , 课程 1
可用

想象一下你在大学当数据分析师(我们不是在做大学数据库嘛,还记得吧?)。有人让你不只是查学生和他们的分数,还要在表里加一列显示小组里的最高分,这样就能方便对比成绩了。怎么搞?当然是用 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;

这里发生了什么:

  1. 对每个学生,我们查出他的名字和分数(s.name, g.grade)。
  2. SELECT MAX(grade) —— 这是个子查询,返回该学生小组里的最高分。
  3. 子查询对主查询的每一行都会执行一次,用 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 里的子查询很强大,但用的时候要注意:

  1. 性能。 每一行主查询都会执行一次子查询。如果表很大,SQL 查询会变慢。比如有1000个学生,子查询就会跑1000次!
  2. 索引。 为了让这种查询快点,最好给子查询 WHERE 里用到的列加索引。
  3. 可读性。 别嵌套太多层。如果子查询太复杂,考虑放到 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 里子查询的优化

我们说过,性能可能会有问题。这里有几个小建议:

  1. 用索引。 如果子查询有过滤条件,记得给相关列加索引。
  2. 缓存结果。 有时候可以把子查询放到 VIEW 或临时表里。
  3. 少嵌套。 能简单就别搞太多层嵌套。

SELECT 里的子查询让你能做很多计算和数据分析。虽然有时候会吃资源,但只要优化好,SQL 就能变得更灵活更强大。大胆试试,找到属于你的查询优化方法吧!

评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION