Как исправить синтаксическую ошибку в динамическом запросе 1С при расчете кратности?

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

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

1. Исправление синтаксиса строковых литералов

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

Проанализируем ошибку: в исходном коде написано "шт" КАК ЕдиницаИзмерения. Если этот текст является частью большой строки, ограниченной символом вертикальной черты |, то платформа воспримет это как окончание строки кода 1С, а не как часть запроса.

Правильный вариант написания строкового значения внутри запроса:

| ""шт"" КАК ЕдиницаИзмерения,

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

2. Пропущенные запятые в списке выбора

Одной из самых частых причин ошибки "Ожидается выражение" является отсутствие запятой между полями в секции ВЫБРАТЬ (SELECT). Посмотрим на фрагмент кода автора:


| КОНЕЦ КАК КоличествоОстаток            
|ВЫРАЗИТЬ(МАКСИМУМ(ТабЦена.Цена...

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

Решение: всегда проверяйте наличие разделителей при склейке строк в цикле.

3. Проблема группировки полей (СГРУППИРОВАТЬ ПО)

Это наиболее глубокая логическая ошибка. В запросе используется секция СГРУППИРОВАТЬ ПО. Согласно правилам SQL и 1С, в этом случае каждое поле, которое перечислено в блоке ВЫБРАТЬ, должно либо быть обернуто в агрегатную функцию (СУММА, МАКСИМУМ, МИНИМУМ, СРЕДНЕЕ), либо присутствовать в секции СГРУППИРОВАТЬ ПО.

Разберем ситуацию с полем КоличествоОстаток. Автор добавил сложный расчет кратности:


|ВЫБОР КОГДА ОстаткиТоваровКомпанииОстатки.КоличествоОстаток / ВЫРАЗИТЬ(...) > 0
|ТОГДА ОстаткиТоваровКомпанииОстатки.Номенклатура.КратностьПоставок * ...

Поле ОстаткиТоваровКомпанииОстатки.КоличествоОстаток участвует в расчете без агрегатной функции, но в секции СГРУППИРОВАТЬ ПО оно отсутствует. Это гарантированно вызовет ошибку выполнения.

У нас есть два пути решения:

  1. Добавить поле в группировку: Это заставит запрос выводить данные в разрезе каждого уникального значения остатка, что может быть неверно с точки зрения логики отчета.
  2. Использовать агрегаты внутри ВЫБОР: Обернуть использование поля в СУММА() или МАКСИМУМ() прямо внутри математического выражения.

4. Безопасное деление и проверка на NULL

При расчете кратности часто выполняется деление (Количество / Кратность). Рассмотрим важный аспект: если КратностьПоставок в справочнике не заполнена (имеет значение NULL или 0), запрос упадет с ошибкой "Деление на 0".

Проанализируем защитную конструкцию. Использовать ЕСТЬNULL(Поле, 0) в знаменателе недостаточно. Необходимо предварительно проверять делитель через ВЫБОР. Рассмотрим правильный пример реализации:


ВЫБОР 
    КОГДА ЕСТЬNULL(ОстаткиТоваровКомпанииОстатки.Номенклатура.КратностьПоставок, 0) = 0 
    ТОГДА 0 
    ИНАЧЕ ОстаткиТоваровКомпанииОстатки.КоличествоОстаток / ОстаткиТоваровКомпанииОстатки.Номенклатура.КратностьПоставок 
КОНЕЦ

5. Особенности функции ВЫРАЗИТЬ для округления

Функция ВЫРАЗИТЬ(Число КАК ЧИСЛО(10,0)) в языке запросов 1С работает не как отсечение дробной части (Floor), а как математическое округление. Например, значение 1.5 превратится в 2. Если ваша задача — получить количество целых упаковок (кратность), простое округление может привести к избытку товара.

Для получения целой части числа (округления "вниз") в запросах часто применяют небольшую хитрость: ВЫРАЗИТЬ(Число - 0.5 КАК ЧИСЛО(10,0)). Выясним, подходит ли это для вашей логики кратности.

6. Формирование динамического текста запроса

Автор собирает запрос в цикле: ТекстЗапроса = ТекстЗапроса + "...". Основная опасность здесь — "склеивание" слов на стыке строк. Если в конце первой строки нет пробела, а вторая начинается сразу с команды ВЫРАЗИТЬ, может получиться каша вида КоличествоОстатокВЫРАЗИТЬ.

Рекомендация: всегда начинайте новую строку в коде 1С с символа вертикальной черты |. Это автоматически добавляет символ переноса строки в результирующую переменную, и парсер запроса сможет корректно распознать инструкции.

7. Исправленный пример структуры запроса

С учетом всех замечаний (запятые, группировки, кавычки), фрагмент формирования запроса должен выглядеть примерно так:


ТекстЗапроса = "ВЫБРАТЬ
|   Остатки.Номенклатура КАК Номенклатура,
|   МАКСИМУМ(Остатки.КоличествоОстаток) КАК КоличествоОстаток,
|   ""шт"" КАК ЕдиницаИзмерения,
|   СУММА(ВЫБОР 
|       КОГДА ЕСТЬNULL(Остатки.Номенклатура.КратностьПоставок, 0) > 0 
|       ТОГДА Остатки.КоличествоОстаток - (Остатки.КоличествоОстаток % Остатки.Номенклатура.КратностьПоставок)
|       ИНАЧЕ Остатки.КоличествоОстаток 
|   КОНЕЦ) КАК КолСУчетомКратности,
|   ВЫРАЗИТЬ(МАКСИМУМ(ТабЦена.Цена * (100 + &Скидка) / 100) КАК ЧИСЛО(15, 2)) КАК Цена
|ИЗ
|   РегистрНакопления.ОстаткиТоваровКомпании.Остатки(&Период, ) КАК Остатки
|...
|СГРУППИРОВАТЬ ПО
|   Остатки.Номенклатура";

Подведем итог: если ваш запрос выдает синтаксическую ошибку после правок, проверьте его в "Консоли запросов" (она лучше подсвечивает место ошибки), обратите внимание на запятые между полями и убедитесь, что все неагрегированные поля включены в секцию СГРУППИРОВАТЬ ПО. Использование динамических параметров вида &Скидка1, &Скидка2 допустимо при небольшом их количестве, но при больших объемах данных лучше использовать загрузку значений во временную таблицу.

← На главную