Скалярный подзапрос

Скалярный подзапрос используется там, где ожидается одно значение. Он должен вернуть не более одной строки и один столбец.

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.

Вопросы

Что будет при двух строках? Ошибка: выражению нужно одно значение.

Как закрепить ожидание «одна строка»? Ограничением уникальности подходящего бизнес-ключа.

Всегда ли коррелированный подзапрос выполняется по одному разу на строку? Не обязательно; оптимизатор может переписать его, поэтому смотрят план.