SELECT 和 WHERE 中的标量子查询
掌握单值子查询,以及子查询返回多行时产生的错误
SELECT 和 WHERE 中的标量子查询 是 CoddyKit 上的免费 Coding Interview Prep 课时。 这是第 1 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Coding Interview Prep 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Coding Interview Prep 课程共包含 4 节课。
面试官所说的标量子查询是什么
标量子查询是一个恰好返回一行一列的查询,也就是一个单独的值。由于它会解析为一个值,数据库允许您几乎在任何可以使用字面值的地方使用它:例如 SELECT、WHERE、HAVING,甚至 ORDER BY 中。
- 面试官会测试您是否了解一行一列这一规则。
- 经典陷阱是:子查询意外返回了多行。
如果您能清楚地陈述这一定义,就已经通过了第一个检查点。
SELECT 列表中的标量子查询
将标量子查询放入 SELECT 列表,可以为每个输出行附加一个计算得到的单独值。这里会在每位员工旁边显示全公司的平均工资。
子查询 (SELECT AVG(salary) FROM employees) 会运行并将整张表汇总为一个数字,然后在每一行中重复显示这个数字。
SELECT
name,
salary,
(SELECT AVG(salary) FROM employees) AS company_avg
FROM employees;WHERE 中的标量子查询
同一个单独的值也可以驱动筛选。面试中非常常见的一道题是:找出所有收入高于公司平均水平的人。
子查询会计算一次平均值,然后将外层查询中的每一行与该平均值进行比较。这不是相关子查询——内层查询不依赖外层行,因此只运行一次。
SELECT name, salary
FROM employees
WHERE salary > (SELECT AVG(salary) FROM employees);“多于一行”错误
这是面试官希望您能够预判的错误。如果子查询与 = 或 > 一起使用,却返回了多行,数据库引擎就会抛出以下错误:
- Postgres:作为表达式使用的子查询返回了多行
- MySQL:子查询返回了多于 1 行
下面的查询会失败,因为部门 5 中可能有多名员工——该子查询不是标量子查询。
SELECT name
FROM employees
WHERE salary = (SELECT salary FROM employees WHERE dept_id = 5);强制使子查询返回标量值
有两种可靠的方法可以保证只得到一个值:
- 使用 MAX、MIN 或 AVG 等聚合函数——不带
GROUP BY的聚合函数始终返回一行。 - 在
ORDER BY之后使用LIMIT 1(Postgres/MySQL)或FETCH FIRST 1 ROW ONLY。
下面的修正版会请求部门 5 中工资最高的员工的单独工资值。
SELECT name
FROM employees
WHERE salary = (
SELECT MAX(salary) FROM employees WHERE dept_id = 5
);没有行时,标量子查询返回 NULL
面试中一个容易忽略的要点是:如果标量子查询匹配到零行,它不会报错——而是返回 NULL。这个 NULL 随后会在比较过程中继续传播。
由于 salary > NULL 的计算结果是 UNKNOWN(而不是真),外层查询不会返回任何行。应试者经常以为这里会报错;正确答案是:结果为空,并且会静默发生。
SELECT name, salary
FROM employees
WHERE salary > (
SELECT AVG(salary) FROM employees WHERE dept_id = 9999
);完整示例:找出高于平均工资的员工及其差额
让我们结合这两种用法。我们会显示每位工资高于平均值的员工,以及其工资高出平均值多少。同一个标量子查询同时出现在 SELECT 和 WHERE 中。
面试官可能会问子查询是否运行了两次。从逻辑上看,它确实出现了两次;但优秀的优化器可以只计算一次这个非相关子查询,然后重复使用结果。
SELECT
name,
salary,
salary - (SELECT AVG(salary) FROM employees) AS above_avg
FROM employees
WHERE salary > (SELECT AVG(salary) FROM employees)
ORDER BY above_avg DESC;ORDER BY 中的标量子查询
由于标量子查询本质上只是一个值,因此也可以合法地放在 ORDER BY 中。这通常不是最清晰的写法,但面试官喜欢借此确认您知道这种用法是允许的。
这里不进行连接,而是按照从另一张表中取得的值——每个部门的员工数量——对部门进行排序。
SELECT d.dept_name
FROM departments d
ORDER BY (
SELECT COUNT(*) FROM employees e WHERE e.dept_id = d.id
) DESC;标量子查询与相关子查询:厘清界限
上面的 ORDER BY 示例实际上引用了外层查询中的 d.id——这使它成为一个相关标量子查询,并且每个外层行都会运行一次。
面试官非常重视下面这个区别:
- 非相关标量子查询:自包含,只运行一次。
- 相关标量子查询:引用外层行,按行运行。
两者仍然都是标量子查询(返回一个值),但性能可能有很大差异。
何时不应使用标量子查询
将相关标量子查询放在 SELECT 列表中很方便,但在大型表上可能较慢——因为它会按行执行。面试官希望您了解以下替代方案:
- 将
LEFT JOIN连接到预先聚合的派生表。 - 使用窗口函数,例如
AVG(salary) OVER ()。
下面的窗口函数写法无需单独扫描子查询,就能生成相同的公司平均工资列。
SELECT
name,
salary,
AVG(salary) OVER () AS company_avg
FROM employees;面试简洁表述
如果面试官要求您定义标量子查询,可以这样回答:“标量子查询返回一行一列,因此它的作用类似于一个单独的值,可以在允许使用字面值的任何地方使用。如果它返回多行,数据库引擎就会报错;如果它不返回任何行,就会得到 NULL。”
这一句话涵盖了定义、错误情况和 NULL 边界情况——也就是每位面试官都会关注的三个要点。
快速检查
测试您对标量子查询行为的理解。
回顾
现在,您已经掌握了标量子查询:
- 定义:一行一列——可以像字面值一样用于 SELECT、WHERE、HAVING 和 ORDER BY。
- 使用 = 或 > 时如果返回多行,就会导致错误;可以通过聚合函数或
LIMIT 1强制得到标量结果。 - 零行会产生 NULL,从而在筛选中静默排除相关行。
- 相关标量子查询会按行运行;当性能很重要时,应优先考虑连接或窗口函数。
下一步:学习 FROM 子句中的子查询,此时结果会成为一整张虚拟表。
常见问题解答
「SELECT 和 WHERE 中的标量子查询」课时是免费的吗?
是的 — 「SELECT 和 WHERE 中的标量子查询」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Coding Interview Prep 课程的其余内容,请升级到 CoddyKit PRO。 Coding Interview Prep 课程共包含 4 节课。
「SELECT 和 WHERE 中的标量子查询」这节课中我会学到什么?
掌握单值子查询,以及子查询返回多行时产生的错误 你通过在浏览器中直接运行的动手代码来练习 Coding Interview Prep,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Coding Interview Prep 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Coding Interview Prep 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 1 节课,共 4 节。
「SELECT 和 WHERE 中的标量子查询」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Coding Interview Prep 课中编写并运行代码吗?
能。每节 Coding Interview Prep 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。
此课程中的所有课时
- SELECT 和 WHERE 中的标量子查询
- FROM 子句中的子查询(派生表)
- IN、ANY 和 ALL 子查询
- EXISTS 与 IN 的性能比较