PostgreSQL 子查询
概述
子查询(Subquery)是嵌套在其他 SQL 查询中的查询语句。子查询可以出现在 WHERE、FROM、SELECT 等子句中,根据使用场景可以返回单个值、单行或多行结果。子查询使得复杂的数据检索逻辑可以在一条 SQL 语句中完成。
WHERE 子查询
IN 子查询
IN 用于检查某个值是否存在于子查询返回的结果集中:
sql
-- 查询属于"电子产品"分类的所有商品
SELECT * FROM products
WHERE category_id IN (
SELECT id FROM categories WHERE name = 'Electronics'
);NOT IN 子查询
NOT IN 用于排除子查询返回结果中的值:
sql
-- 查询没有取消订单的用户
SELECT * FROM users
WHERE id NOT IN (
SELECT user_id FROM orders WHERE status = 'cancelled'
);注意
当子查询结果中包含 NULL 值时,NOT IN 会返回空结果集。此时应改用 NOT EXISTS。
EXISTS 子查询
EXISTS 用于检查子查询是否返回至少一行数据:
sql
-- 查询至少有一笔订单的用户
SELECT * FROM users u
WHERE EXISTS (
SELECT 1 FROM orders o WHERE o.user_id = u.id
);NOT EXISTS 子查询
NOT EXISTS 用于检查子查询是否不返回任何数据:
sql
-- 查询没有任何评价的商品
SELECT * FROM products p
WHERE NOT EXISTS (
SELECT 1 FROM reviews r WHERE r.product_id = p.id
);ANY / SOME 子查询
ANY(或 SOME)用于将值与子查询返回的任意一个值进行比较,只要满足其中一个条件即为真:
sql
-- 查询价格高于任一分类平均价格的商品
SELECT * FROM products
WHERE price > ANY (
SELECT AVG(price) FROM products GROUP BY category
);ALL 子查询
ALL 用于将值与子查询返回的所有值进行比较,必须满足全部条件才为真:
sql
-- 查询价格高于所有分类最高价格的商品
SELECT * FROM products
WHERE price > ALL (
SELECT MAX(price) FROM products GROUP BY category
);FROM 子查询(派生表)
子查询可以放在 FROM 子句中作为临时表(也称为派生表或内联视图)使用:
sql
-- 查询商品数量超过10的分类统计信息
SELECT * FROM (
SELECT
category,
COUNT(*) AS product_count,
AVG(price) AS avg_price
FROM products
GROUP BY category
) AS category_stats
WHERE product_count > 10;提示
FROM 子句中的子查询必须使用 AS 指定别名。
SELECT 子查询(标量子查询)
子查询可以放在 SELECT 子句中,作为计算列使用。此时子查询必须返回单个值(标量值):
sql
-- 查询每个用户及其订单数量
SELECT
name,
(SELECT COUNT(*) FROM orders WHERE user_id = users.id) AS order_count
FROM users;相关子查询
相关子查询(Correlated Subquery)是指子查询引用了外部查询的列。每处理外部查询的一行数据,子查询都会重新执行一次:
sql
-- 查询有大额订单(金额超过1000)的用户
SELECT u.* FROM users u
WHERE EXISTS (
SELECT 1 FROM orders o
WHERE o.user_id = u.id
AND o.total > 1000
);sql
-- 查询每个分类中价格最高的商品
SELECT p.* FROM products p
WHERE p.price = (
SELECT MAX(price)
FROM products
WHERE category = p.category
);小结
子查询要点:
WHERE子句中可使用IN、EXISTS、ANY、ALL等操作符FROM子句中的子查询作为派生表使用,必须指定别名SELECT子句中的标量子查询用于计算列,必须返回单个值- 相关子查询引用外部查询的列,每行都会重新执行
NOT IN遇到NULL值时行为特殊,建议优先使用NOT EXISTS