CodeGym /课程 /SQL SELF /使用子查询时的常见错误

使用子查询时的常见错误

SQL SELF
第 14 级 , 课程 4
可用

用子查询其实就像在玩脱衣象棋:一开始看着挺简单,直到你下错一步就尴尬了。那这些错误都从哪来的?大多是因为没搞懂语法、忽略了 SQL 的逻辑细节,或者就是手滑没注意。这节课我们就聊聊最常见的错误,还有怎么避免它们。

语法错误

子查询对语法要求很严。漏了逗号、括号或者 alias,整个查询都能崩掉。来看看几个典型问题。

漏括号

括号在用子查询时超级重要。子查询要用圆括号包起来,哪怕少一对都能让 SQL 报语法错。

错误示例:

SELECT student_name
FROM students
WHERE student_id IN SELECT student_id FROM enrollments);

错误:

ERROR:  syntax error at or near "SELECT"

修正:

SELECT student_name
FROM students
WHERE student_id IN (SELECT student_id FROM enrollments);

小贴士:IN 里的查询一定要用括号包起来,这样 SQL 才知道你要执行子查询。

缺少 alias

如果你在 FROM 里用子查询,记得一定要给它起个 alias。不然 PostgreSQL 会懵逼。

错误示例:

SELECT student_name, avg_score
FROM (SELECT student_id, AVG(score) AS avg_score FROM grades GROUP BY student_id)
WHERE avg_score > 80;

错误:

ERROR:  subquery in FROM must have an alias

修正:

SELECT student_name, avg_score
FROM (SELECT student_id, AVG(score) AS avg_score FROM grades GROUP BY student_id) AS subquery
WHERE avg_score > 80;

小贴士:PostgreSQL 要求每个临时表(FROM 里的子查询结果)都得有个名字。

性能问题

子查询,尤其是没优化好的那种,能让你的数据库变得超级慢。性能问题大多是因为重复计算或者没加索引。

重复计算

SELECT 里的子查询会对每一行都算一遍,时间成本爆炸。

示例:

SELECT student_name,
       (SELECT COUNT(*) FROM enrollments WHERE enrollments.student_id = students.student_id) AS course_count
FROM students;

如果 students 表有几万行,这个子查询会每行都重新跑一遍。

优化:

WITH course_counts AS (
    SELECT student_id, COUNT(*) AS course_count
    FROM enrollments
    GROUP BY student_id
)
SELECT s.student_name, c.course_count
FROM students s
LEFT JOIN course_counts c ON s.student_id = c.student_id;

CTE(Common Table Expression)或者 join 能避免每行都重复算。这个方法我们后面还会讲到 :P

没加索引

如果你在 WHERE 里用复杂子查询,记得要给相关字段加索引。

示例:

SELECT student_name
FROM students
WHERE student_id IN (SELECT student_id FROM enrollments WHERE course_id = 10);

如果 enrollments 表里的 student_id 字段没索引,子查询会全表扫描,超级慢。

优化: 建个索引:

CREATE INDEX idx_enrollments_course_id ON enrollments (course_id);

你是不是已经听过无数次“要加索引”了?但别急,后面还会详细讲。索引很重要,不过它们主要是帮你加速查询,不是让烂查询变好查询。它们能让你的查询在 production 上飞起来,尤其是表有几百万行的时候。

逻辑错误

子查询里的逻辑错误其实也很常见。比如没搞懂 NULL 怎么处理、过滤条件写错、聚合函数用错等等。

NULL 用错了

NULL 是个大坑,专坑新手。用 INNOT IN 做子查询时,NULL 的存在会影响结果。

错误示例:

SELECT student_name
FROM students
WHERE student_id NOT IN (SELECT student_id FROM enrollments);

如果 enrollments 表里有 student_id = NULL 的行,这个查询啥都查不出来。因为 NOT IN 条件会变成 NULL IS NOT IN,结果就全挂了。

修正:

SELECT student_name
FROM students
WHERE student_id NOT IN (SELECT student_id FROM enrollments WHERE student_id IS NOT NULL);

NOT IN 时一定要先过滤掉 NULL

过滤条件写错

子查询经常用来做复杂过滤,但条件写错了,结果就完全不是你想要的。

错误示例:

SELECT student_name
FROM students
WHERE (SELECT AVG(score) FROM grades WHERE grades.student_id = students.student_id) > 80;

如果有学生没有成绩,子查询会返回 NULL,这个学生就查不出来了。

修正:

SELECT student_name
FROM students
WHERE COALESCE((SELECT AVG(score) FROM grades WHERE grades.student_id = students.student_id), 0) > 80;

COALESCENULL 换成默认值。

防止出错的小建议

想避免用子查询时踩坑,记住这些:

括号和 alias 要用对。 有问题先检查括号是不是都闭合了,子查询有没有 alias。

优化查询。 能用 JOINWITH 或索引的地方就别用子查询。

注意 NULL。 子查询里可能有 NULL,要用 IS NOT NULLCOALESCE 或类似的写法处理。

多测试。 每个子查询都单独跑一下,看看结果是不是你想要的。

代码要好读。 多用缩进和 alias,让你的代码更清楚。记住:一个月后你自己都可能看不懂自己写的啥。

1
调查/小测验
子查询的使用第 14 级,课程 4
不可用
子查询的使用
子查询的使用
评论 (2)
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION
Coder 级别 6
25 七月 2025
如果IN子查询里有NULL值会怎样?这道题的答案是什么。我记得NOT IN的时候才会返回空集
Coder 级别 6
25 七月 2025
如果 enrollments 表里的 student_id 字段没索引,子查询会全表扫描,超级慢。这句话的student_id应该是course_id吧