Как правильно добавить условие в динамический запрос 1С и избежать синтаксических ошибок

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

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

Выясняем причину ошибки при конкатенации строк

Рассмотрим ситуацию, с которой столкнулся автор темы. В коде была предпринята попытка добавить условие ГДЕ следующим образом:


ТекстЗапроса = ТекстЗапроса + "
|    И (__Документ.Касса = "+ОсновнаяКасса+"
|            ИЛИ __Документ.КассаОтправитель = "+ОсновнаяКасса+")";

Проанализируем, что происходит в этот момент внутри системы. Переменная ОсновнаяКасса содержит ссылку на объект справочника. Когда мы пытаемся сложить строку и ссылку, 1С автоматически преобразует ссылку в строку (вызывает строковое представление объекта). Если касса называется, например, "Основная касса GSL", то итоговый текст запроса превратится в:

И (__Документ.Касса = Основная касса GSL ИЛИ ...

Для встроенного языка запросов 1С текст Основная касса GSL является бессмысленным набором слов, так как он не заключен в кавычки, не является функцией или параметром. Система ожидает завершения логического выражения (закрывающей скобки), но встречает непонятный текст, что и приводит к ошибке "Ожидается выражение )". Кроме того, даже если бы мы добавили кавычки, запрос сравнивал бы ссылку с текстом, что логически неверно.

Шаг 1: Переход к использованию параметров запроса

Правильный подход заключается в использовании параметров. Параметр в языке запросов помечается символом амперсанда (&). Это позволяет отделить логику запроса от конкретных данных. Перепишем фрагмент добавления условия, заменив прямую вставку на имя параметра:


ТекстЗапроса = ТекстЗапроса + "
|    И (__Документ.Касса = &ПараметрОсновнаяКасса
|            ИЛИ __Документ.КассаОтправитель = &ПараметрОсновнаяКасса)";

Важный момент: Обратите внимание на пробелы при склейке строк. Мы всегда рекомендуем начинать новую строку в текстовом блоке с символа вертикальной черты |, что гарантирует наличие необходимых отступов и предотвращает "слипание" ключевых слов запроса (например, ГДЕ__Документ вместо ГДЕ __Документ).

Шаг 2: Установка параметров в объекте ПостроительОтчета

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

Поскольку ТекстЗапроса — это переменная типа Строка, у нее нет и не может быть метода УстановитьПараметр. Этот метод принадлежит объекту Запрос, но у ПостроителяОтчета работа с параметрами реализована через специальное свойство Параметры, которое является структурой.

Рассмотрим правильный код для передачи значения:


// Сначала присваиваем текст построителю
ПостроительОтчета.Текст = ТекстЗапроса;

// Затем устанавливаем параметры через коллекцию параметров построителя
ПостроительОтчета.Параметры.Вставить("ПараметрОсновнаяКасса", ОсновнаяКасса);

// Только после этого заполняем настройки
ПостроительОтчета.ЗаполнитьНастройки();

Использование метода Вставить() для структуры параметров гарантирует, что если параметр с таким именем уже существовал, он будет обновлен, а если нет — создан.

Шаг 3: Проверка типов данных и псевдонимов

Проанализируем еще одну потенциальную проблему, которая часто всплывает при динамической сборке — использование псевдонимов в секции УПОРЯДОЧИТЬ ПО. В исходном сообщении автора фигурировала строка УПОРЯДОЧИТЬ ПО По_документ.дата. Это также может вызвать ошибку, если в секции ВЫБРАТЬ поле не было названо именно так.

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

  1. Если поле выбирается напрямую: УПОРЯДОЧИТЬ ПО __Документ.Дата (используя псевдоним таблицы).
  2. Если для поля задан псевдоним (КАК ДатаДокумента), то: УПОРЯДОЧИТЬ ПО ДатаДокумента.

Посмотрим на исправленный и дополненный фрагмент кода целиком, чтобы закрепить материал:


// Формируем текст запроса
ТекстЗапроса = "ВЫБРАТЬ
|    __Документ.Ссылка,
|    __Документ.Дата КАК Дата,
|    __Документ.Номер КАК Номер
|ИЗ
|    Документ." + ИмяДокумента + " КАК __Документ
|ГДЕ
|    __Документ.Дата >= НАЧАЛОПЕРИОДА(&ДатаНачала, ДЕНЬ)
|    И (__Документ.Дата <= КОНЕЦПЕРИОДА(&ДатаОкончания, ДЕНЬ) ИЛИ &ДатаОкончания = ДАТАВРЕМЯ(1,1,1))";

// Добавляем наше специфическое условие
ТекстЗапроса = ТекстЗапроса + "
|    И (__Документ.Касса = &ОсновнаяКасса 
|       ИЛИ __Документ.КассаОтправитель = &ОсновнаяКасса)";

// Упорядочивание
ТекстЗапроса = ТекстЗапроса + "
|УПОРЯДОЧИТЬ ПО Дата";

// Работа с Построителем
ПостроительОтчета.Текст = ТекстЗапроса;
ПостроительОтчета.Параметры.Вставить("ДатаНачала", ДатаНачалаПериодаЖурнала);
ПостроительОтчета.Параметры.Вставить("ДатаОкончания", ДатаОкончанияПериодаЖурнала);
ПостроительОтчета.Параметры.Вставить("ОсновнаяКасса", ОсновнаяКасса);

ПостроительОтчета.ЗаполнитьНастройки();

Дополнительные советы по оптимизации

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

Рассмотрим пример универсального условия:


|ГДЕ
|    (&ОсновнаяКасса = ЗНАЧЕНИЕ(Справочник.Кассы.ПустаяСсылка) 
|     ИЛИ __Документ.Касса = &ОсновнаяКасса)

Такой подход позволяет запросу игнорировать фильтр, если параметр не задан, что делает ваш код более надежным и удобным для пользователя.

Подведем итоги:

Соблюдение этих правил позволит вам избежать трудночитаемых ошибок компиляции запроса и значительно упростит отладку кода в будущем.

← На главную