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