В процессе разработки сложных отчетов и механизмов обработки данных программисты часто сталкиваются с необходимостью динамического выбора данных в зависимости от условий. Конструкция ВЫБОР (аналог CASE в SQL) является мощным инструментом, но её использование имеет ряд синтаксических и архитектурных ограничений. Разберем подробно, можно ли вставить запрос внутрь этой конструкции, как это сделать правильно и какие существуют альтернативы для оптимизации производительности.
Для начала проанализируем базовый синтаксис. В языке запросов 1С конструкция ВЫБОР работает с выражениями. Главное правило заключается в том, что результат, возвращаемый в секциях ТОГДА или ИНАЧЕ, должен быть скалярным. Это означает, что подзапрос, если мы решим его там использовать, обязан возвращать ровно одно значение (одну колонку и одну строку).
Если мы попытаемся вставить в ТОГДА текст запроса, который выбирает таблицу данных, платформа выдаст ошибку синтаксиса. Рассмотрим, почему это происходит: СУБД должна четко понимать тип данных колонки еще до начала выполнения выборки. Если одна строка вернет число, а другая — результат подзапроса из десяти строк, структура результирующей таблицы будет нарушена.
Наиболее часто подзапросы внутри ВЫБОР используются для проверки условий. Рассмотрим пример, предложенный в обсуждении, когда нам нужно проверить наличие значения в определенном наборе данных:
ВЫБОР
КОГДА 1 В (ВЫБРАТЬ 1 ИЗ Справочник.Номенклатура ГДЕ Ссылка = &НужнаяСсылка)
ТОГДА "Найдено"
ИНАЧЕ "Не найдено"
КОНЕЦ
В данном случае подзапрос возвращает логическое значение (истина, если запись существует). Это допустимый вариант использования. Однако стоит помнить о производительности: такой запрос называется коррелированным подзапросом. Проанализируем ситуацию: если в основной выборке 10 000 строк, то СУБД может выполнить вложенный запрос 10 000 раз, что крайне негативно скажется на скорости работы базы данных.
В сообщении №7 пользователь пытался вставить агрегатную функцию СУММА внутрь ВЫБОР. Это классическая ошибка новичка. Вместо того чтобы писать логику «если статья такая-то, то вычисли сумму», в 1С принято использовать паттерн условной агрегации. Выясним, как это выглядит на практике.
Правильный подход — поместить ВЫБОР внутрь функции СУММА. Посмотрим на пример кода:
СУММА(ВЫБОР
КОГДА СтатьиДвиженияДенежныхСредств.Ссылка = ЗНАЧЕНИЕ(Справочник.СтатьиДвиженияДенежныхСредств.ВыдачаДенежныхСредствКассеККМ)
ТОГДА ДвиженияДенежныхСредствОбороты.СуммаОборот
ИНАЧЕ 0
КОНЕЦ) КАК СуммаПоСпецСтатье
Этот метод позволяет за один проход по таблице собрать данные по разным условиям в разные колонки, не создавая лишних вложенных запросов.
Если вам все же нужно подтянуть данные из другого источника по сложному условию (как в сообщении №12), лучше всего использовать ЛЕВОЕ СОЕДИНЕНИЕ. Разберем по шагам, как это реализовать:
ЕСТЬNULL, чтобы корректно обработать строки, для которых во второй таблице не нашлось данных.Пример реализации:
ВЫБРАТЬ
ОсновнойЗапрос.Статья,
ЕСТЬNULL(ДопСуммы.СуммаОборот, ОсновнойЗапрос.СуммаОборот) КАК ИтоговаяСумма
ИЗ
РегистрНакопления.ДвиженияДенежныхСредств.Обороты(&Начало, &Конец) КАК ОсновнойЗапрос
ЛЕВОЕ СОЕДИНЕНИЕ (
ВЫБРАТЬ
Статья,
СУММА(СуммаОборот) КАК СуммаОборот
ИЗ
РегистрНакопления.ДвиженияДенежныхСредств.Обороты(&Начало, &Конец)
ГДЕ
Статья В (&СписокДопСтатей)
СГРУППИРОВАТЬ ПО
Статья
) КАК ДопСуммы
ПО ОсновнойЗапрос.Статья = ДопСуммы.Статья
В некоторых случаях, когда логика выбора данных слишком разная для разных условий, проще использовать ОБЪЕДИНЕНИЕ ВСЕ. Это позволяет разделить один сложный запрос с множеством ВЫБОР на несколько простых и понятных частей.
Рассмотрим ситуацию: для одной статьи мы берем данные из одного регистра, а для всех остальных — из другого. Вместо нагромождения конструкций ВЫБОР, мы напишем два запроса и объединим их результаты. Это упростит чтение кода и позволит СУБД построить более эффективный план выполнения.
Если вы работаете в СКД, архитектура решения может быть еще изящнее. Проанализируем возможности компоновки:
ВЫБОР на уровень настроек СКД. Это позволит пользователю самому управлять тем, как отображаются данные.При работе с вложенными конструкциями всегда придерживайтесь следующих правил:
ЕСТЬNULL(..., 0) или ЕСТЬNULL(..., ""). Это предотвратит ошибки при арифметических операциях.МЕНЕДЖЕР ВРЕМЕННЫХ ТАБЛИЦ. Это позволит проиндексировать поля связи и значительно ускорить выборку.ВЫБОР КОГДА (ВЫБОР КОГДА...) может привести к ошибке переполнения стека парсера SQL-сервера.Таким образом, хотя технически вставить скалярный подзапрос в ВЫБОР можно, в 90% случаев это является признаком неоптимального проектирования. Использование ЛЕВОЕ СОЕДИНЕНИЕ или ОБЪЕДИНЕНИЕ ВСЕ сделает ваш код более надежным, быстрым и легким для поддержки коллегами.