При разработке сложных отчетов или обработок программисты часто сталкиваются с необходимостью изменять логику отбора данных в зависимости от выбора пользователя. Распространенная ошибка новичков — попытка передать часть логического выражения (например, И Номенклатура = &Значение) напрямую через параметр запроса. В этой статье мы разберем, почему такой подход не работает, и рассмотрим правильные методы реализации динамических условий.
Прежде всего, проанализируем ситуацию: платформа 1С воспринимает параметры запроса (конструкции, начинающиеся с &) исключительно как значения определенных типов (число, строка, ссылка, дата). Когда вы пытаетесь написать ГДЕ &Условие, система ожидает, что в &Условие будет передано булево значение (Истина или Ложь), а не фрагмент программного кода на языке запросов.
Если же ваша цель — менять само тело запроса (выбирать разные поля или разные операторы сравнения), то обычные параметры здесь не помогут. Попытка вставить логическую конструкцию в параметр приведет к ошибке: "Операция не разрешена в предложении ГДЕ".
Самый простой и часто используемый метод — это сборка текста запроса как обычной строки в коде на встроенном языке. Рассмотрим, как это реализовать на практике. Допустим, нам нужно в зависимости от условий выбирать разные типы расхождений в инвентаризации.
Разберем по шагам, как модифицировать исходный запрос:
ТекстУсловия = "";
Если ТипФильтра = 1 Тогда
ТекстУсловия = " И (НЕ ТоварыВРознице.СуммаПродажнаяКонечныйОстаток = Инвентаризация.СуммаРознФакт
| ИЛИ НЕ ТоварыВРознице.СуммаПродажнаяКонечныйОстаток = Инвентаризация.СуммаРознУчет)";
ИначеЕсли ТипФильтра = 2 Тогда
ТекстУсловия = " И НЕ ТоварыВРознице.СуммаПродажнаяКонечныйОстаток = Инвентаризация.СуммаРознФакт";
ИначеЕсли ТипФильтра = 3 Тогда
ТекстУсловия = " И НЕ ТоварыВРознице.СуммаПродажнаяКонечныйОстаток = Инвентаризация.СуммаРознУчет";
КонецЕсли;
ТекстЗапроса =
"ВЫБРАТЬ
| ТоварыВРознице.Номенклатура,
| ТоварыВРознице.СуммаПродажнаяКонечныйОстаток,
| Инвентаризация.СуммаРознФакт
|ИЗ
| РегистрНакопления.ТоварыВРознице.ОстаткиИОбороты(&Дата1, &Дата2, , , ) КАК ТоварыВРознице
|ВНУТРЕННЕЕ СОЕДИНЕНИЕ Документ.ИнвентаризацияТоваровНаСкладе.Товары КАК Инвентаризация
|ПО ТоварыВРознице.Номенклатура = Инвентаризация.Номенклатура
|ГДЕ
| ТоварыВРознице.Склад = Инвентаризация.Ссылка.Склад" + ТекстУсловия;
Важный момент: при использовании такого подхода следите за пробелами в начале добавляемой строки, чтобы текст запроса не "склеился" в нечитаемую конструкцию.
Если вы не хотите менять текст запроса программно, можно использовать логику внутри самого SQL-запроса с помощью функции ВЫБОР. Мы передаем в параметр идентификатор режима (например, число 1, 2 или 3), а запрос "решает", какое условие применить.
Рассмотрим пример реализации:
ГДЕ
ВЫБОР
КОГДА &Режим = 1 ТОГДА ТоварыВРознице.Сумма <> Инвентаризация.СуммаФакт
КОГДА &Режим = 2 ТОГДА ТоварыВРознице.Сумма <> Инвентаризация.СуммаУчет
ИНАЧЕ ИСТИНА
КОНЕЦ
Проанализируем ситуацию с производительностью: данный метод кажется удобным, но он имеет существенный недостаток. СУБД (например, MS SQL Server) сложнее оптимизировать такой запрос. Индексы могут перестать использоваться эффективно, что приведет к замедлению работы на больших объемах данных. Поэтому для высоконагруженных систем рекомендуется использовать динамическую сборку текста (Способ 1).
Если ваш запрос используется в отчете на базе СКД, то платформа предоставляет гораздо более элегантный способ — использование фигурных скобок {}. Это позволяет сделать условия необязательными.
Выясним, как это работает. В тексте запроса СКД вы можете написать:
ВЫБРАТЬ
Номенклатура,
СуммаОстаток
ИЗ
РегистрНакопления.ТоварыНаСкладах.Остатки
{ГДЕ
Номенклатура.*,
Склад.*}
В этом случае, если пользователь в настройках отчета не установил отбор по складу, платформа автоматически удалит это условие из финального текста запроса, который отправится на сервер базы данных. Это самый правильный и производительный способ для построения отчетов.
Для продвинутых задач существует объект СхемаЗапроса. Он позволяет работать с запросом как с объектной моделью, а не как со строкой. Мы можем программно добавлять условия в коллекцию отборов, не заботясь о расстановке кавычек и соединительных союзов И / ИЛИ.
Рассмотрим краткий пример:
Схема = Новый СхемаЗапроса;
Схема.УстановитьТекст(ТекстВашегоЗапроса);
ОператорВыбора = Схема.Запросы[0].Операторы[0];
// Добавляем новое условие в секцию ГДЕ
ОператорВыбора.Отбор.Добавить("Номенклатура = &ВыбНоменклатура");
Запрос.Текст = Схема.ПолучитьТекст();
Использование СхемаЗапроса минимизирует риск синтаксических ошибок, которые часто возникают при ручной склейке строк.
ОстаткиИОбороты), а не в секцию ГДЕ. Это значительно ускоряет выборку.ЛЕВОЕ СОЕДИНЕНИЕ или ПОЛНОЕ СОЕДИНЕНИЕ отсутствующие данные заменяются значением NULL. Любое сравнение с NULL (например, Сумма <> 100) даст результат "Ложь", даже если сумма фактически не равна 100. Используйте функцию ЕСТЬNULL(Поле, 0).ДОБАВИТЬКДАТЕ и НАЧАЛОПЕРИОДА внутри запроса, лучше заранее рассчитать начало и конец нужного периода в коде 1С и передать их как готовые параметры даты.НЕ Поле = Значение. Используйте стандартный оператор "не равно" — <>. Это делает код более читаемым и привычным для SQL-оптимизаторов.Подводя итог, можно сказать: выбор метода зависит от контекста. Для простых обработок подойдет динамическая сборка строки, для сложных отчетов идеальным выбором будет СКД, а для систем, требующих максимальной надежности и гибкости — Схема запроса.