把一条查询嵌套在另一条之中,以便与聚合值比较、按集合过滤或检测是否存在。
在 Kodokon 中打开本课一个子查询是放在括号里、嵌套于另一条查询内部的查询。在单条语句中,它就能回答诸如“金额高于平均值的订单”或“至少有一个订单的客户”这样的问题。开始之前,请在 sqliteonline.com 或 sqlite3 中运行这段脚本。
CREATE TABLE customers (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL
);
CREATE TABLE orders (
id INTEGER PRIMARY KEY,
customer_id INTEGER NOT NULL,
amount REAL NOT NULL
);
INSERT INTO customers (id, name) VALUES
(1, 'Alice'), (2, 'Karim'),
(3, 'Mina'), (4, 'Paulo');
INSERT INTO orders (id, customer_id, amount)
VALUES
(1, 1, 49.90), (2, 1, 15.00),
(3, 2, 120.50), (4, 2, 80.00),
(5, 3, 9.90), (6, 1, 200.00);放在 WHERE 中的标量子查询必须返回单个值:一行一列。引擎会先计算它,再像使用任何其他常量那样使用它。你无法用简单的 WHERE amount > AVG(amount) 来做到这一点,这在 SQL 中是被禁止的。
SELECT id, customer_id, amount
FROM orders
WHERE amount > (
SELECT AVG(amount) FROM orders
);IN 会把某一列与子查询返回的值集合进行比较。EXISTS 则检测相关子查询(它引用了 c.id,即外层查询的一列)是否至少返回一行:一旦找到第一个匹配,引擎就会停下。
SELECT name FROM customers
WHERE id IN (
SELECT customer_id FROM orders
WHERE amount > 100
);
SELECT c.name FROM customers AS c
WHERE EXISTS (
SELECT 1 FROM orders AS o
WHERE o.customer_id = c.id
);在 FROM 中,子查询表现得像一张临时表,称为派生表,并且必须被赋予一个别名。它是分两步聚合的经典工具:先算出每位客户的总额,再算出这些总额的平均值。
SELECT ROUND(AVG(t.total), 2) AS avg_basket
FROM (
SELECT customer_id, SUM(amount) AS total
FROM orders
GROUP BY customer_id
) AS t;