При работе с запросами в 1С часто возникает задача преобразования числовых результатов к определенному формату. Для этого используется функция ВЫРАЗИТЬ. Однако, если исходное число не помещается в заданный формат, весь запрос прерывается с ошибкой СУБД, чаще всего с кодом "22003" (numeric value out of range). Чтобы упростить работу разработчика и провести быстрый анализ данных, давайте подробно разберем, как можно проверить значение прямо в тексте запроса и избежать этой ошибки.
Проанализируем типичную ситуацию:
необходимо рассчитать процент наценки и привести его к типу ЧИСЛО(5, 2). Но из-за ошибок ввода или специфики данных результат может оказаться слишком большим и не поместиться в этот тип.
Прежде чем исправлять ошибку, важно понять ее механику. Конструкция ВЫРАЗИТЬ(МоеЧисло КАК ЧИСЛО(5, 2)) говорит системе, что итоговое число должно иметь всего не более 5 знаков, из которых 2 знака отведены под дробную часть. В этом контексте будет очень полезна шпаргалка по языку запросов, которая наглядно описывает подобные синтаксические особенности.
Это означает, что диапазон допустимых значений для этого типа: от -999.99 до 999.99.
Что произойдет, если результат вычисления будет, например, 999.995? Согласно правилам математического округления, это число будет округлено до 1000.00. А число 1000.00 уже состоит из 6 знаков (4 до запятой и 2 после). Оно физически не помещается в тип ЧИСЛО(5, 2), что и вызывает ошибку переполнения на уровне СУБД.
Наша задача — перехватить такие значения до того, как функция ВЫРАЗИТЬ попытается их обработать.
Самый надежный и производительный способ решить проблему внутри запроса — использовать условную конструкцию ВЫБОР ... КОГДА ... ИНАЧЕ ... КОНЕЦ. Она позволяет проанализировать значение перед тем, как выполнять над ним потенциально опасные операции. Проверить корректность логики и быстро отладить результат поможет консоль запросов для управляемых форм.
Рассмотрим по шагам, как построить правильную проверку.
Идея проста: если мы знаем, что результат вычислений может превысить допустимый предел, мы проверяем его. Если он в пределах нормы — применяем ВЫРАЗИТЬ, если нет — выводим какое-то альтернативное значение (например, специальное число, строку или NULL).
Начальный вариант может выглядеть так:
ВЫБОР
КОГДА (Цена - БазоваяЦена) / БазоваяЦенаСрав * 100 > 999
ТОГДА "Слишком большое значение"
ИНАЧЕ
ВЫРАЗИТЬ((Цена - БазоваяЦена) / БазоваяЦенаСрав * 100 КАК ЧИСЛО(5, 2))
КОНЕЦ
Однако эта проверка не совсем точна. Она не учитывает правила округления.
Как мы выяснили, критической точкой является значение, которое при округлении до двух знаков даст 1000.00. Это любое число, которое больше или равно 999.995. Именно его и нужно использовать в условии.
Правильная проверка будет выглядеть так:
ВЫБОР
КОГДА (Цена - БазоваяЦена) / БазоваяЦенаСрав * 100 >= 999.995
ТОГДА NULL // Или 999.99, или другое значение-маркер
ИНАЧЕ
ВЫРАЗИТЬ((Цена - БазоваяЦена) / БазоваяЦенаСрав * 100 КАК ЧИСЛО(5, 2))
КОНЕЦ
Теперь запрос корректно отсекает все значения, которые могут вызвать переполнение. Вместо ошибки мы получим NULL (или другое указанное нами значение), которое можно будет обработать в коде 1С уже после выполнения запроса.
Ошибка переполнения — не единственная, которая может возникнуть в нашем выражении. Чтобы сделать запрос по-настоящему надежным, нужно предусмотреть еще как минимум два случая: деление на ноль и наличие пустых (NULL) значений в полях.
1. Защита от деления на ноль
Если поле БазоваяЦенаСрав окажется равным нулю, запрос также завершится ошибкой. Проверку на ноль следует добавить в нашу конструкцию ВЫБОР, причем перед проверкой на переполнение. Это важно, так как если знаменатель равен нулю, вычислять основное выражение уже нельзя.
2. Обработка NULL значений
Если поля Цена или БазоваяЦена могут быть не заполнены, они будут содержать значение NULL. Любая арифметическая операция с NULL (например, NULL - 100) в результате дает NULL. Чтобы этого избежать и считать незаполненные значения нулями, используем функцию ЕСТЬNULL().
Соберем все вместе и получим итоговый, "промышленный" вариант запроса:
ВЫБОР
// Шаг 1: Проверяем знаменатель на ноль. Это самая первая и важная проверка.
КОГДА ЕСТЬNULL(БазоваяЦенаСрав, 0) = 0
ТОГДА 0 // Если делить не на что, считаем результат нулевым
// Шаг 2: Проверяем на переполнение. Эта проверка сработает, только если первая не выполнилась.
КОГДА (ЕСТЬNULL(Цена, 0) - ЕСТЬNULL(БазоваяЦена, 0)) / БазоваяЦенаСрав * 100 >= 999.995
ТОГДА 999.99 // Возвращаем максимально допустимое значение как маркер
// Шаг 3: Если все проверки пройдены, выполняем безопасное преобразование.
ИНАЧЕ
ВЫРАЗИТЬ((ЕСТЬNULL(Цена, 0) - ЕСТЬNULL(БазоваяЦена, 0)) / БазоваяЦенаСрав * 100 КАК ЧИСЛО(5, 2))
КОНЕЦ КАК ПроцентНаценки
Такая многоступенчатая проверка гарантирует, что запрос выполнится без ошибок в самых разных ситуациях.
ВЫРАЗИТЬНа форумах иногда можно встретить предложение проверять ошибку путем сравнения результата преобразования к разным типам.
Рассмотрим пример неправильного подхода:
// НЕПРАВИЛЬНЫЙ ПОДХОД, КОТОРЫЙ
ВЫЗОВЕТ ОШИБКУ!
ВЫБОР
КОГДА ВЫРАЗИТЬ(МоеЧисло КАК ЧИСЛО(5, 2)) <> ВЫРАЗИТЬ(МоеЧисло КАК ЧИСЛО(15, 2))
ТОГДА "Ошибка"
ИНАЧЕ
ВЫРАЗИТЬ(МоеЧисло КАК ЧИСЛО(5, 2))
КОНЕЦ
Этот подход не работает. Причина в том, что СУБД для выполнения сравнения (оператор <>) должна сначала вычислить оба операнда. Она попытается выполнить ВЫРАЗИТЬ(МоеЧисло КАК ЧИСЛО(5, 2)), и если МоеЧисло слишком большое, ошибка переполнения произойдет именно на этом этапе, еще до того, как начнется само сравнение.
Напоследок рассмотрим, о чем еще стоит подумать:
ЧИСЛО(10, 2), что позволит хранить гораздо большие значения.Таким образом, использование конструкции ВЫБОР КОГДА с правильной последовательностью проверок (на ноль, на переполнение) является самым эффективным способом защиты запросов 1С от ошибок преобразования типов.