Что произойдёт при выполнении скалярного коррелированного подзапроса, если для одной строки внешнего запроса он вернёт несколько строк?
Скалярный подзапрос обязан вернуть не более одной строки для каждой строки внешнего запроса. Если корреляция допускает несколько совпадений, большинство СУБД завершит запрос ошибкой о неоднозначном результате, а не выберет строку произвольно.
Если подзапрос не найдёт ни одной строки, скалярный результат обычно будет NULL. Поэтому перед использованием такого подзапроса нужно доказать однозначность связи или явно свести несколько строк к одному значению.
Подзапросы позволяют выразить вычисление внутри другого запроса, не создавая отдельный промежуточный объект в базе данных. Коррелированный подзапрос расширяет эту модель: его результат может зависеть от текущей строки внешнего запроса.
Скалярная форма нужна, когда для каждой внешней строки требуется одно значение: последнее событие, сумма, минимальная дата или вычисленный признак. Ограничение «не более одной строки» защищает смысл выражения: одна ячейка результата не должна самопроизвольно превращаться в набор строк.
Предположим, для каждого клиента нужно получить значение из связанной таблицы. Если связь «клиент — запись» фактически является отношением «один ко многим», простой коррелированный подзапрос может вернуть несколько строк.
Риск состоит не только в технической ошибке. Попытка скрыть неоднозначность выбором произвольной строки может сделать отчёт нестабильным: результат начнёт зависеть от плана выполнения, индекса или физического порядка строк. Если бизнес-правило требует одну конкретную запись, оно должно быть явно выражено в запросе.
Скалярный подзапрос вычисляется в контексте текущей строки внешнего запроса. Значения внешнего запроса используются как параметры корреляции, поэтому один и тот же подзапрос может дать разные результаты для разных строк.
Его кардинальность должна быть такой:
NULL;Минимальный пример:
Если у клиента несколько заказов, выражение amount не имеет единственного значения, поэтому большинство СУБД выдаст ошибку. Исправление зависит от смысла задачи:
Агрегат превращает несколько строк в одно значение и тем самым восстанавливает скалярность. Однако MAX корректен только тогда, когда именно максимум соответствует бизнес-правилу; он не означает «последний заказ».
Для выбора одной записи по критерию обычно нужна упорядоченная логика выбора, например через оконную функцию или предварительно построенную производную таблицу. Простое ограничение одной строки без детерминированного порядка может скрыть ошибку данных и дать непредсказуемый результат.
Коррелированный подзапрос также может быть менее удобен для оптимизации, поскольку логически связан с каждой строкой внешнего результата. Оптимизатор нередко преобразует его в полу-соединение, агрегацию или другой эквивалентный план, но полагаться на обязательное построчное выполнение нельзя.
В отчёте по клиентам требовалась сумма всех оплаченных заказов. Изначально разработчик использовал скалярный подзапрос, возвращающий сумму заказа напрямую. После появления второго оплаченного заказа запрос начал завершаться ошибкой.
Рассматривались два варианта. Первый — ограничить подзапрос одной строкой; это устраняло ошибку, но отбрасывало часть данных и не определяло, какой заказ считать значимым. Второй — агрегировать оплаченные заказы по клиенту до получения результата; этот вариант сохранял все данные и точно соответствовал требованию «сумма всех заказов».
Выбрали второй вариант. Для задачи с одним значением на клиента агрегация явно обеспечила нужную кардинальность, а отсутствие заказов сохранило корректную семантику пустого результата как NULL, который при необходимости можно отдельно преобразовать в ноль.
1. Чем отличается отсутствие строк от нескольких строк в скалярном подзапросе?
Отсутствие строк обычно преобразуется в NULL, потому что скалярное выражение всё равно должно иметь значение. Несколько строк не имеют однозначного скалярного представления, поэтому обычно приводят к ошибке. Это принципиально отличается от IN или EXISTS, которые по своей природе рассчитаны на множество строк.
2. Почему добавление агрегата не всегда является правильным исправлением?
Агрегат устраняет ошибку кардинальности, но одновременно меняет смысл результата. MAX, MIN, SUM и COUNT отвечают на разные вопросы; выбор агрегата должен следовать бизнес-правилу. Если требуется конкретная запись, например последняя по времени, одной агрегации значения может быть недостаточно: нужно сначала однозначно выбрать запись по критерию.
3. Почему ограничение одной строкой без сортировки опасно?
Ограничение количества строк без детерминированного порядка не определяет, какая строка будет выбрана. СУБД может вернуть разные строки после изменения плана, индекса или статистики, даже если данные не менялись. Такой приём допустим только при гарантированной единственности результата либо при явно заданном и однозначном критерии выбора.