子查询:WHERE、FROM 与 SELECT 中的子查询
什么是子查询
子查询(Subquery)是嵌套在另一个查询中的 SELECT 语句。
基本形式:
SELECT column
FROM table
WHERE column = (SELECT ...);子查询用括号包裹,可以出现在 WHERE、FROM、SELECT、HAVING 等位置。
WHERE 中的标量子查询
标量子查询返回单个值(一行一列)。
示例 1:查询高于平均金额的订单
SELECT product, amount
FROM orders
WHERE amount > (SELECT AVG(amount) FROM orders);结果:
product | amount
---------+---------
Laptop | 1200.00
Monitor | 300.00工作原理:
- 子查询计算平均金额:
(SELECT AVG(amount) FROM orders)→ 331 - 主查询使用这个值:
WHERE amount > 331
示例 2:查询特定用户的所有订单
SELECT product, amount
FROM orders
WHERE user_id = (SELECT id FROM users WHERE name = 'Alice');结果:
product | amount
-----------+---------
Laptop | 1200.00
Keyboard | 80.00标量子查询的要求
子查询必须返回单个值,否则报错:
-- 错误:子查询返回多行
SELECT * FROM orders
WHERE user_id = (SELECT id FROM users WHERE name LIKE 'A%');错误信息:
ERROR: more than one row returned by a subquery used as an expression解决方法:使用 IN(下一节)或限制子查询结果。
WHERE 中的 IN 子查询
IN 子查询可以返回多行(一列)。
-- 查询名字以 'A' 开头的用户的所有订单
SELECT product, amount
FROM orders
WHERE user_id IN (SELECT id FROM users WHERE name LIKE 'A%');结果:
product | amount
-----------+---------
Laptop | 1200.00
Keyboard | 80.00等价于 JOIN:
SELECT o.product, o.amount
FROM orders o
INNER JOIN users u ON o.user_id = u.id
WHERE u.name LIKE 'A%';何时用子查询,何时用 JOIN?
- 子查询:逻辑更清晰,适合简单过滤
- JOIN:性能通常更好,适合需要多表列的情况
FROM 中的子查询(派生表)
子查询可以作为临时表使用:
SELECT customer, total
FROM (
SELECT u.name AS customer, SUM(o.amount) AS total
FROM users u
INNER JOIN orders o ON u.id = o.user_id
GROUP BY u.name
) AS user_totals
WHERE total > 100;结果:
customer | total
----------+---------
Alice | 1280.00
Charlie | 300.00说明:
- 内部子查询生成临时表
user_totals - 外部查询从这个临时表中筛选
必须给派生表起别名(AS user_totals),否则报错。
派生表的用途
解决"不能在 WHERE 中使用聚合函数"的问题:
-- 错误:WHERE 不能用 HAVING
SELECT u.name, SUM(o.amount) AS total
FROM users u
INNER JOIN orders o ON u.id = o.user_id
GROUP BY u.name
WHERE SUM(o.amount) > 100; -- 错误!正确写法(用派生表):
SELECT customer, total
FROM (
SELECT u.name AS customer, SUM(o.amount) AS total
FROM users u
INNER JOIN orders o ON u.id = o.user_id
GROUP BY u.name
) AS t
WHERE total > 100;或者用 HAVING(更简洁):
SELECT u.name, SUM(o.amount) AS total
FROM users u
INNER JOIN orders o ON u.id = o.user_id
GROUP BY u.name
HAVING SUM(o.amount) > 100;SELECT 中的子查询
可以在 SELECT 列表中使用子查询:
SELECT
name,
(SELECT COUNT(*) FROM orders WHERE user_id = users.id) AS order_count,
(SELECT SUM(amount) FROM orders WHERE user_id = users.id) AS total_spent
FROM users;结果:
name | order_count | total_spent
---------+-------------+-------------
Alice | 2 | 1280.00
Bob | 2 | 75.00
Charlie | 1 | 300.00
Diana | 0 |这是相关子查询(Correlated Subquery):子查询引用了外部查询的列(users.id)。
等价于 LEFT JOIN + GROUP BY:
SELECT u.name, COUNT(o.id) AS order_count, SUM(o.amount) AS total_spent
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
GROUP BY u.name;JOIN 版本通常性能更好。
相关子查询 vs 非相关子查询
非相关子查询
子查询独立执行,不依赖外部查询:
SELECT * FROM orders
WHERE amount > (SELECT AVG(amount) FROM orders);执行过程:
- 执行子查询一次:
SELECT AVG(amount) FROM orders→ 331 - 用结果执行主查询
相关子查询
子查询依赖外部查询,对每行重复执行:
SELECT name,
(SELECT COUNT(*) FROM orders WHERE user_id = users.id)
FROM users;执行过程:
- 对 users 的每一行
- 执行子查询,传入当前行的
users.id - 返回结果
性能影响:相关子查询可能很慢(对每行重复执行)。
子查询与比较运算符
可以用 >, <, >=, <= 与子查询结合:
-- 查询金额大于所有 Bob 订单的订单
SELECT product, amount
FROM orders
WHERE amount > ALL (SELECT amount FROM orders WHERE user_id = 2);ALL:大于所有值(即大于最大值)
-- 查询金额大于任意一个 Bob 订单的订单
SELECT product, amount
FROM orders
WHERE amount > ANY (SELECT amount FROM orders WHERE user_id = 2);ANY:大于任意值(即大于最小值)
等价写法:
-- ALL
WHERE amount > (SELECT MAX(amount) FROM orders WHERE user_id = 2)
-- ANY
WHERE amount > (SELECT MIN(amount) FROM orders WHERE user_id = 2)嵌套子查询
子查询可以嵌套多层:
SELECT name
FROM users
WHERE id IN (
SELECT user_id
FROM orders
WHERE amount > (
SELECT AVG(amount)
FROM orders
)
);建议:避免过深嵌套(影响可读性和性能),考虑用 JOIN 或 CTE(后续章节)。
子查询的常见用途
1. 动态过滤条件
-- 查询销售额前 20% 的产品
SELECT product
FROM orders
WHERE amount >= (SELECT PERCENTILE_CONT(0.8) WITHIN GROUP (ORDER BY amount) FROM orders);2. 复杂聚合
-- 每个用户消费占总消费的百分比
SELECT
u.name,
SUM(o.amount) AS spent,
ROUND(SUM(o.amount) / (SELECT SUM(amount) FROM orders) * 100, 2) AS percentage
FROM users u
INNER JOIN orders o ON u.id = o.user_id
GROUP BY u.name;3. 数据完整性检查
-- 查询没有任何订单的用户
SELECT name
FROM users
WHERE id NOT IN (SELECT DISTINCT user_id FROM orders WHERE user_id IS NOT NULL);或用 NOT EXISTS(更高效):
SELECT name
FROM users u
WHERE NOT EXISTS (SELECT 1 FROM orders o WHERE o.user_id = u.id);子查询 vs JOIN
| 特性 | 子查询 | JOIN |
|---|---|---|
| 可读性 | 逻辑清晰 | 需要理解连接 |
| 性能 | 相关子查询可能慢 | 通常更快 |
| 功能 | 可嵌套,灵活 | 受限于表关系 |
| 优化 | 取决于数据库 | 优化器擅长 |
经验法则:
- 简单过滤:子查询
- 需要多表列:JOIN
- 复杂逻辑:根据可读性选择
练习
标量子查询:查询金额高于平均值的订单,显示产品和金额。
IN 子查询:查询在 2026-01-17 之后下过单的用户名。
派生表:先计算每个用户的订单数,再查询订单数 >= 2 的用户。
SELECT 子查询:查询每个用户的姓名和订单数(使用 SELECT 中的子查询)。
相关子查询:查询每个用户最贵的订单产品。
NOT IN:查询没有下过单的用户。
ANY/ALL:查询金额大于所有 Electronics 类别订单的订单。
嵌套子查询:查询购买了最贵产品的用户名。
完成练习后,打开本章答案页核对。
下一步
学会了子查询。下一节学习 EXISTS 和 IN 的深入对比,以及如何高效检查存在性。