При разработке отчетов и обработок в среде «1С:Предприятие» программисты часто сталкиваются с необходимостью кастомизации запросов. Типичная задача — реализовать вывод различных типов цен в зависимости от характеристик номенклатуры (например, штучный это товар или весовой). Однако при написании условий в тексте запроса легко совершить синтаксическую ошибку, которая приведет к аварийному завершению программы или некорректному расчету данных.
В этой статье мы подробно разберем, почему возникает ошибка неопознанного оператора при работе со строками в запросах, как правильно оформлять кавычки, а также рассмотрим профессиональные приемы оптимизации подобных запросов, включая защиту от деления на ноль и уход от жесткого кодирования (хардкода).
Рассмотрим ситуацию, с которой столкнулся автор темы. В кодовой базе обработки была предпринята попытка сделать выборку цены с помощью следующей конструкции в тексте запроса:
| ВЫБОР
| КОГДА ВложенныйЗапрос.ШтукаКод = 000000003
| ТОГДА ВложенныйЗапрос.Цена2
| ИНАЧЕ ВложенныйЗапрос.Цена
| КОНЕЦ КАК Цена,
При выполнении такого кода программа либо выдавала неверный результат (всегда срабатывала ветка ИНАЧЕ), либо возвращала ошибку. Выясним причину этого поведения. Реквизит ШтукаКод имеет строковый тип данных (так как это код элемента справочника). В языке запросов 1С сравнение строки с числом напрямую невозможно. Запись вида 000000003 воспринимается СУБД как число 3, в то время как код в базе данных хранится как текстовая строка "000000003".
Когда автор попытался исправить ситуацию и добавил стандартные кавычки прямо в текст модуля:
| КОГДА ВложенныйЗапрос.ШтукаКод = "000000003"
Конфигуратор 1С выдал ошибки компиляции вида «Неопознанный оператор» и «Ошибка в выражении». Это произошло потому, что символ одинарной двойной кавычки внутри строкового литерала в модуле воспринимается платформой как завершение строки кода 1С. В результате синтаксический анализатор платформы ломает структуру модуля и пытается интерпретировать оставшуюся часть запроса как команды встроенного языка.
Самый быстрый способ исправить синтаксическую ошибку прямо в тексте запроса — использовать правила экранирования символов встроенного языка 1С. Если нам необходимо поместить символ двойной кавычки внутрь строки, ограниченной кавычками, мы должны продублировать этот символ. То есть написать две кавычки подряд.
Проанализируем корректный вариант написания этого условия в модуле 1С:
Запрос.Текст = "ВЫБРАТЬ
| ...
| ВЫБОР
| КОГДА ВложенныйЗапрос.ШтукаКод = ""000000003""
| ТОГДА ВложенныйЗапрос.Цена2
| ИНАЧЕ ВложенныйЗапрос.Цена
| КОНЕЦ КАК Цена,
| ..." ;
При таком подходе платформа успешно скомпилирует модуль, а СУБД получит строку запроса, в которой условие будет выглядеть корректно: ШтукаКод = '000000003' (в терминах SQL-синтаксиса).
Хотя дублирование кавычек решает проблему компиляции, прописывание конкретных текстовых кодов непосредственно в теле запроса (хардкод) считается крайне плохим стилем разработки. Во-первых, такой запрос невозможно нормально редактировать в визуальном Конструкторе запросов — он будет постоянно сбивать кавычки или выдавать ошибки. Во-вторых, код элемента справочника в базе данных может измениться, и тогда придется переписывать программный код обработки.
Рекомендуем переписать логику с использованием параметров запроса. Это делает код чистым, понятным и избавляет от необходимости возиться с кавычками.
Разберем по шагам, как это реализовать:
&:
| ВЫБОР
| КОГДА ВложенныйЗапрос.ШтукаКод = &КодШтучногоТовара
| ТОГДА ВложенныйЗапрос.Цена2
| ИНАЧЕ ВложенныйЗапрос.Цена
| КОНЕЦ КАК Цена,
Запрос.УстановитьПараметр("КодШтучногоТовара", "000000003");
При таком подходе код становится универсальным, читаемым, а Конструктор запросов будет работать без каких-либо сбоев.
Использование конкретного кода "000000003" для определения того, является ли единица измерения штукой, несет в себе скрытые риски. При переносе обработки в другую базу данных, при свертке базы или синхронизации этот код может измениться или принадлежать совсем другому элементу.
Более надежным решением является проверка кода международной классификации единиц измерения (ОКЕИ). Для штук стандартным кодом по классификатору является "796", а для килограммов — "166". Эти коды стандартизированы и не меняются от базы к базе.
Давайте посмотрим, как можно переписать условие проверки по коду ОКЕИ базовой единицы измерения:
| ВЫБОР
| КОГДА ВложенныйЗапрос.ЕдиницаИзмерения.ЕдиницаПоКлассификатору.Код = "796"
| ТОГДА ВложенныйЗапрос.Цена2
| ИНАЧЕ ВложенныйЗапрос.Цена
| КОНЕЦ КАК Цена,
Если в вашей конфигурации признак штучного или весового товара определяется через категорию или специальное свойство (как указано в исходном коде: БПак.Номенклатура.Категория), то наиболее правильным решением будет передавать в параметр запроса саму ссылку на категорию, а не ее строковый код. Например:
Запрос.УстановитьПараметр("КатегорияШтука", Справочники.КатегорииНоменклатуры.НайтиПоКоду("000000003"));
А в самом запросе выполнять сравнение ссылочных типов:
| ВЫБОР
| КОГДА ВложенныйЗапрос.Номенклатура.Категория = &КатегорияШтука
| ТОГДА ВложенныйЗапрос.Цена2
| ИНАЧЕ ВложенныйЗапрос.Цена
| КОНЕЦ КАК Цена,
Проанализировав исходный текст запроса из обращения автора, мы обнаружили серьезную уязвимость в расчете цены весового товара во вложенном запросе:
(1 / БПак.Номенклатура.ЕдиницаХраненияОстатков.Вес) * (БПак.Цена * &Курс / &Кратность) КАК Цена
Внимание! Если в базе данных найдется хотя бы один товар, у которого в карточке не заполнен вес единицы хранения (или он равен нулю), СУБД мгновенно прервет выполнение запроса с системной ошибкой «Деление на ноль» (Division by zero). Вся обработка полностью перестанет работать у пользователей.
Чтобы обезопасить систему, обязательно нужно добавить проверку веса на равенство нулю или значению NULL с помощью оператора ВЫБОР. Перепишем этот фрагмент запроса безопасным образом:
| ВЫБОР
| КОГДА ЕСТЬNULL(БПак.Номенклатура.ЕдиницаХраненияОстатков.Вес, 0) = 0
| ТОГДА 0
| ИНАЧЕ (1 / БПак.Номенклатура.ЕдиницаХраненияОстатков.Вес) * (БПак.Цена * &Курс / &Кратность)
| КОНЕЦ КАК Цена,
Внедрение этой простой проверки гарантирует стабильную работу вашей обработки даже в условиях незаполненных или некорректных данных в справочнике номенклатуры.
В исходном запросе также присутствует строка динамической сборки текста:
"ВложенныйЗапрос.Номенклатура." + ТоварКод + " КАК ТоварКод,"
Такой прием часто используется в универсальных отчетах, где пользователь сам выбирает, какой уникальный идентификатор выводить на печать (например, Артикул, Код или Штрихкод). При работе с такими конструкциями обязательно следуйте правилам безопасности:
ТоварКод на заполненность перед тем, как компоновать строку запроса. Если переменная окажется пустой, запрос сформирует синтаксическую ошибку.ВЫРАЗИТЬ к динамическим полям.Пример безопасной инициализации динамического поля в коде 1С:
ИмяПоляКода = СокращеннаяЛП(ТоварКод);
Если ПустаяСтрока(ИмяПоляКода) Тогда
ИмяПоляКода = "Код"; // Значение по умолчанию, если настройка не задана
КонецЕсли;
Запрос.Текст = "ВЫБРАТЬ
| ВложенныйЗапрос.Номенклатура." + ИмяПоляКода + " КАК ТоварКод,
| ...";
Мы подробно разобрали причины возникновения синтаксических ошибок при работе со строками в запросах 1С, изучили правила экранирования кавычек, внедрили параметры для повышения читаемости кода и защитили систему от возможных сбоев СУБД. Применяйте эти стандарты разработки на практике для создания надежных и производительных решений!