Скалярный подзапрос
Скалярный подзапрос используется там, где ожидается одно значение. Он должен вернуть не более одной строки и один столбец.
SELECT id, total
FROM orders
WHERE total > (
SELECT AVG(total)
FROM orders
);Агрегат без GROUP BY возвращает одну строку даже для пустого набора; значение AVG тогда будет NULL, и условие не пропустит ни одной строки.
Коррелированный вариант
SELECT o.id, o.user_id, o.total
FROM orders AS o
WHERE o.total > (
SELECT AVG(o2.total)
FROM orders AS o2
WHERE o2.user_id = o.user_id
);Внутренний запрос связан с текущей строкой внешнего. Оптимизатор может преобразовать его в соединение или другой эффективный план, но полагаться на это без EXPLAIN нельзя.
Гарантия одной строки
Плохо:
WHERE user_id = (SELECT id FROM users WHERE status = 'ACTIVE')Если активных пользователей несколько, СУБД выдаст ошибку. Если бизнес-правило утверждает уникальность, его нужно закрепить UNIQUE, а не надеяться на данные.
Ловушки
- Ноль строк обычно превращается в
NULL; больше одной — ошибка. LIMIT 1безORDER BYне определяет, какая строка выбрана.- Случайный скалярный подзапрос в
SELECTможет выполниться логически для множества строк и стать узким местом.
Связано: Подзапросы с несколькими строками и одним столбцом, Что лучше подзапросы или JOIN, Ограничения, constraints.
Вопросы
Что будет при двух строках? Ошибка: выражению нужно одно значение.
Как закрепить ожидание «одна строка»? Ограничением уникальности подходящего бизнес-ключа.
Всегда ли коррелированный подзапрос выполняется по одному разу на строку? Не обязательно; оптимизатор может переписать его, поэтому смотрят план.