Можно ли в конструкцию ВЫБОР вставить запрос в языке запросов 1С

Программист 1С v8.3 (Управляемые формы) Управленческий учет IT и автоматизация бизнеса
← На главную

В процессе разработки сложных отчетов и механизмов обработки данных программисты часто сталкиваются с необходимостью динамического выбора данных в зависимости от условий. Конструкция ВЫБОР (аналог CASE в SQL) является мощным инструментом, но её использование имеет ряд синтаксических и архитектурных ограничений. Разберем подробно, можно ли вставить запрос внутрь этой конструкции, как это сделать правильно и какие существуют альтернативы для оптимизации производительности.

Ограничения конструкции ВЫБОР и понятие скалярности

Для начала проанализируем базовый синтаксис. В языке запросов 1С конструкция ВЫБОР работает с выражениями. Главное правило заключается в том, что результат, возвращаемый в секциях ТОГДА или ИНАЧЕ, должен быть скалярным. Это означает, что подзапрос, если мы решим его там использовать, обязан возвращать ровно одно значение (одну колонку и одну строку).

Если мы попытаемся вставить в ТОГДА текст запроса, который выбирает таблицу данных, платформа выдаст ошибку синтаксиса. Рассмотрим, почему это происходит: СУБД должна четко понимать тип данных колонки еще до начала выполнения выборки. Если одна строка вернет число, а другая — результат подзапроса из десяти строк, структура результирующей таблицы будет нарушена.

Использование подзапроса в секции КОГДА

Наиболее часто подзапросы внутри ВЫБОР используются для проверки условий. Рассмотрим пример, предложенный в обсуждении, когда нам нужно проверить наличие значения в определенном наборе данных:


ВЫБОР
    КОГДА 1 В (ВЫБРАТЬ 1 ИЗ Справочник.Номенклатура ГДЕ Ссылка = &НужнаяСсылка)
    ТОГДА "Найдено"
    ИНАЧЕ "Не найдено"
КОНЕЦ

В данном случае подзапрос возвращает логическое значение (истина, если запись существует). Это допустимый вариант использования. Однако стоит помнить о производительности: такой запрос называется коррелированным подзапросом. Проанализируем ситуацию: если в основной выборке 10 000 строк, то СУБД может выполнить вложенный запрос 10 000 раз, что крайне негативно скажется на скорости работы базы данных.

Проблема агрегатных функций внутри ВЫБОР

В сообщении №7 пользователь пытался вставить агрегатную функцию СУММА внутрь ВЫБОР. Это классическая ошибка новичка. Вместо того чтобы писать логику «если статья такая-то, то вычисли сумму», в 1С принято использовать паттерн условной агрегации. Выясним, как это выглядит на практике.

Правильный подход — поместить ВЫБОР внутрь функции СУММА. Посмотрим на пример кода:


СУММА(ВЫБОР
    КОГДА СтатьиДвиженияДенежныхСредств.Ссылка = ЗНАЧЕНИЕ(Справочник.СтатьиДвиженияДенежныхСредств.ВыдачаДенежныхСредствКассеККМ)
    ТОГДА ДвиженияДенежныхСредствОбороты.СуммаОборот
    ИНАЧЕ 0
КОНЕЦ) КАК СуммаПоСпецСтатье

Этот метод позволяет за один проход по таблице собрать данные по разным условиям в разные колонки, не создавая лишних вложенных запросов.

Альтернатива №1: Левое соединение с вложенным запросом

Если вам все же нужно подтянуть данные из другого источника по сложному условию (как в сообщении №12), лучше всего использовать ЛЕВОЕ СОЕДИНЕНИЕ. Разберем по шагам, как это реализовать:

  1. Сначала мы подготавливаем данные во временной таблице или во вложенном запросе, рассчитывая необходимые суммы или выбирая нужные значения.
  2. Затем мы соединяем основную таблицу с этой подготовленной выборкой по ключевым полям (например, по статье движения денежных средств).
  3. В основном поле выбора используем функцию ЕСТЬNULL, чтобы корректно обработать строки, для которых во второй таблице не нашлось данных.

Пример реализации:


ВЫБРАТЬ
    ОсновнойЗапрос.Статья,
    ЕСТЬNULL(ДопСуммы.СуммаОборот, ОсновнойЗапрос.СуммаОборот) КАК ИтоговаяСумма
ИЗ
    РегистрНакопления.ДвиженияДенежныхСредств.Обороты(&Начало, &Конец) КАК ОсновнойЗапрос
ЛЕВОЕ СОЕДИНЕНИЕ (
    ВЫБРАТЬ
        Статья,
        СУММА(СуммаОборот) КАК СуммаОборот
    ИЗ
        РегистрНакопления.ДвиженияДенежныхСредств.Обороты(&Начало, &Конец)
    ГДЕ
        Статья В (&СписокДопСтатей)
    СГРУППИРОВАТЬ ПО
        Статья
) КАК ДопСуммы
ПО ОсновнойЗапрос.Статья = ДопСуммы.Статья

Альтернатива №2: ОБЪЕДИНЕНИЕ ВСЕ

В некоторых случаях, когда логика выбора данных слишком разная для разных условий, проще использовать ОБЪЕДИНЕНИЕ ВСЕ. Это позволяет разделить один сложный запрос с множеством ВЫБОР на несколько простых и понятных частей.

Рассмотрим ситуацию: для одной статьи мы берем данные из одного регистра, а для всех остальных — из другого. Вместо нагромождения конструкций ВЫБОР, мы напишем два запроса и объединим их результаты. Это упростит чтение кода и позволит СУБД построить более эффективный план выполнения.

Особенности работы в Системе компоновки данных (СКД)

Если вы работаете в СКД, архитектура решения может быть еще изящнее. Проанализируем возможности компоновки:

  1. Вычисляемые поля: Вы можете вынести логику ВЫБОР на уровень настроек СКД. Это позволит пользователю самому управлять тем, как отображаются данные.
  2. Несколько наборов данных: Вместо сложного соединения внутри текста запроса, вы можете создать два разных набора данных (Query datasets) и связать их на закладке "Связи наборов данных". СКД сама выполнит необходимые соединения, зачастую более оптимально, чем это сделал бы программист вручную.

Важные рекомендации по производительности

При работе с вложенными конструкциями всегда придерживайтесь следующих правил:

Таким образом, хотя технически вставить скалярный подзапрос в ВЫБОР можно, в 90% случаев это является признаком неоптимального проектирования. Использование ЛЕВОЕ СОЕДИНЕНИЕ или ОБЪЕДИНЕНИЕ ВСЕ сделает ваш код более надежным, быстрым и легким для поддержки коллегами.

← На главную