Как в запросе 1С вывести все записи, если параметр (список значений) не заполнен?

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

В практике разработки на платформе 1С:Предприятие часто возникает задача: реализовать фильтр в отчете или обработке так, чтобы при выбранных значениях (например, конкретных складах) данные фильтровались, а при пустом списке — выводились все записи без исключения. Попытка решить это "в лоб" внутри языка запросов часто приводит к ошибкам типов или неоптимальной работе системы. Рассмотрим подробно, как правильно реализовать эту логику разными способами.

Почему не работает простое условие в запросе?

Разберем типичную ошибку начинающего разработчика. Часто пытаются написать условие так: ВЫБОР КОГДА &Склад <> ЗНАЧЕНИЕ(Справочник.Склады.ПустаяСсылка) ТОГДА Склад В (&Склад) ИНАЧЕ ИСТИНА КОНЕЦ. Проблема здесь в том, что параметр &Склад в данном контексте является коллекцией (Массив или СписокЗначений), а ЗНАЧЕНИЕ(Справочник.Склады.ПустаяСсылка) — это одиночная ссылка. Система компоновки или SQL-сервер не могут сравнить массив с одиночным значением, что вызывает ошибку несовместимости типов.

Проанализируем ситуацию: нам нужно, чтобы условие ГДЕ либо ограничивало выборку, либо становилось "прозрачным" (всегда истинным), если отбор не задан. Рассмотрим самые эффективные методы решения.

Метод 1: Использование дополнительного параметра-флага

Этот способ считается классическим и наиболее понятным. Мы заранее определяем на стороне встроенного языка, заполнен ли наш список, и передаем в запрос логический флаг (Булево). Рассмотрим пример реализации (для быстрой подготовки кода можно использовать Консоль кода с ИИ-помощником):


// Определяем, заданы ли склады для фильтрации
СкладыЗаданы = (СписокСкладов.Количество() > 0);

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

Запрос.УстановитьПараметр("СкладыЗаданы", СкладыЗаданы);
Запрос.УстановитьПараметр("СписокСкладов", СписокСкладов);

Результат = Запрос.Выполнить();

Преимущества этого метода: простота написания и стабильность (для быстрой отладки подойдет консоль запросов для управляемых форм). Мы явно управляем логикой запроса. Недостатки: на очень больших таблицах конструкция ВЫБОР в блоке ГДЕ может помешать оптимизатору СУБД использовать индексы (запрос перестанет быть SARGable), что приведет к замедлению работы.

Метод 2: Динамическое формирование текста запроса

Проанализируем более профессиональный подход, который используют опытные разработчики для оптимизации производительности. Мы будем изменять сам текст запроса в зависимости от того, заполнен ли параметр. Если список пуст, мы просто не будем добавлять условие ГДЕ.


ТекстЗапроса = 
    "ВЫБРАТЬ
    |   СвободныеОстатки.Номенклатура,
    |   СвободныеОстатки.В Наличии
    |ИЗ
    |   РегистрНакопления.СвободныеОстатки КАК СвободныеОстатки
    |{ГДЕ_УСЛОВИЕ}";

Если СписокСкладов.Количество() > 0 Тогда
    ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "{ГДЕ_УСЛОВИЕ}", "ГДЕ СвободныеОстатки.Склад В (&СписокСкладов)");
    Запрос.УстановитьПараметр("СписокСкладов", СписокСкладов);
Иначе
    ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "{ГДЕ_УСЛОВИЕ}", "");
КонецЕсли;

Запрос.Текст = ТекстЗапроса;

Выясним причину, по которой этот метод лучше: СУБД получает "чистый" SQL-запрос. Если склады не выбраны, в запросе вообще нет лишних проверок, и система просто читает данные. Если склады выбраны, используется стандартный оператор IN, который отлично оптимизируется индексами.

Метод 3: Использование Построителя запроса или СКД

Рассмотрим вариант, когда платформа сама берет на себя управление фильтрами. Если вы используете Систему компоновки данных (СКД), вам вообще не нужно писать условия "если пусто — всё". Достаточно добавить поле в "Отбор" или сделать параметр необязательным. Платформа сама проанализирует настройки пользователя и, если галочка напротив склада не стоит, исключит это условие из итогового SQL-запроса.

Если же вы работаете в коде, можно использовать ПостроительЗапроса. Посмотрим на пример:


Построитель = Новый ПостроительЗапроса;
Построитель.Текст = 
    "ВЫБРАТЬ
    |   Остатки.Номенклатура КАК Номенклатура,
    |   Остатки.Склад КАК Склад
    |ИЗ
    |   РегистрНакопления.ТоварыНаСкладах.Остатки КАК Остатки
    |{ГДЕ
    |   Остатки.Склад.* КАК Склад}";

// Если мы НЕ добавим отбор программно, запрос выберет ВСЕ склады
Если СписокСкладов.Количество() > 0 Тогда
    НовыйОтбор = Построитель.Отбор.Добавить("Склад");
    НовыйОтбор.ВидСравнения = ВидСравнения.ВСписке;
    НовыйОтбор.Значение = СписокСкладов;
    НовыйОтбор.Использование = Истина;
КонецЕсли;

Построитель.Выполнить();
Результат = Построитель.Результат;

Символы {} в тексте запроса указывают построителю (или СКД) на то, что этот блок является необязательным. Это наиболее "правильный" с точки зрения архитектуры 1С способ.

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

Проанализируем еще несколько нюансов, которые помогут избежать ошибок при работе с большими объемами данных:

  1. Ограничение на количество элементов: Помните, что оператор В (&Список) в SQL имеет свои пределы (обычно около 1000–2000 элементов). Если ваш список складов или товаров может содержать десятки тысяч позиций, запрос может завершиться аварийно. В таких случаях лучше использовать временные таблицы.
  2. Временные таблицы: Посмотрим, как это работает. Мы сначала загружаем список во временную таблицу, а потом соединяем ее с основной таблицей данных.
    
    МенеджерВТ = Новый МенеджерВременныхТаблиц;
    ЗапросВТ = Новый Запрос("ВЫБРАТЬ Ссылка ПОМЕСТИТЬ ВТ_Склады ИЗ &МассивСкладов");
    ЗапросВТ.МенеджерВременныхТаблиц = МенеджерВТ;
    ЗапросВТ.УстановитьПараметр("МассивСкладов", СписокСкладов);
    ЗапросВТ.Выполнить();
    
  3. Логическое "ИЛИ": Иногда используют конструкцию ГДЕ (НЕ &ИспользоватьОтбор) ИЛИ (Склад В (&Список)). Она работает, но часто вызывает "сканирование таблицы" (Table Scan) вместо "поиска по индексу" (Index Seek), что существенно замедляет отчет на больших базах.

Подведем итог: для простых задач и небольших таблиц идеально подходит Метод 1 (Флаг в запросе). Если же вы разрабатываете высоконагруженную систему или сложный отчет, лучше всего использовать Метод 2 (Динамический текст) или стандартные механизмы СКД.

← На главную