Вкладывай один запрос в другой, чтобы сравнить с агрегатом, отфильтровать по множеству или проверить существование.
Открыть этот урок в 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;