При разработке отчетов и обработок в системе 1С:Предприятие одной из наиболее частых задач является расчет относительных показателей, в частности — процентного соотношения двух числовых величин. На первый взгляд задача кажется тривиальной и сводится к школьной формуле деления одного числа на другое с последующим умножением на 100. Однако при работе с реальными базами данных разработчик неизбежно сталкивается с техническими нюансами: наличием пустых значений NULL, риском возникновения критической ошибки деления на ноль и необходимостью красивого округления полученного результата.
В рамках данной статьи мы подробно разберем, как правильно реализовать этот расчет на уровне запроса 1С, как избежать падения системы при делении на ноль, а также рассмотрим профессиональный подход к выводу процентов с использованием возможностей Системы компоновки данных (СКД).
Представим стандартную ситуацию, описанную автором темы. В результате выполнения запроса мы получаем две числовые колонки (назовем их Колонка1 и Колонка2). Нам требуется получить третью колонку, в которой будет отображаться процентное отношение второй колонки к первой.
Давайте проанализируем исходные данные из примера:
Выясним причину несоответствия во второй строке. Автор темы допустил классическую ментальную ошибку из-за невнимательности или усталости. Он интуитивно продолжил делить значение второй строки на фиксированную базу первой строки (1000), посчитав, что там также указана тысяча («думал 1к написал»). При делении 420 на 1000 мы действительно получили бы 42%. Однако реальный расчет для второй строки выглядит следующим образом: 420 / 1200 * 100 = 35%.
Этот пример наглядно показывает, что доверять «ручному» расчету в уме нельзя, а алгоритмы программы должны быть абсолютно строгими и учитывать любые изменения в исходных данных.
Прежде чем писать код запроса, разберем ключевые опасности, подстерегающие разработчика при работе с СУБД (будь то MS SQL Server, PostgreSQL, IBM DB2, Oracle или файловая база данных 1С):
1. Деление на ноль. Если в Колонка1 (знаменатель) окажется значение 0, то любая СУБД мгновенно прервет выполнение запроса и вернет критическую ошибку выполнения (Division by zero). Это приведет к аварийному завершению работы отчета у пользователя.
2. Значения NULL. Если данные выбираются из левого или полного соединения таблиц, в полях неизбежно будут появляться незаполненные значения NULL. Любая арифметическая операция с участием NULL (например, NULL / 5 или 100 * NULL) в результате всегда дает NULL. Хотя это не вызывает аварийного завершения запроса, на выходе мы получим пустые ячейки в отчете, что выглядит некрасиво и затрудняет дальнейший анализ.
Рассмотрим подробнее, как реализовать расчет непосредственно на языке запросов 1С. Для того чтобы обойти указанные выше проблемы, нам необходимо использовать комбинацию встроенных функций запроса: ВЫБОР, ЕСТЬNULL и ВЫРАЗИТЬ.
Посмотрим на пример безопасного построения запроса:
ВЫБРАТЬ
ИсходныеДанные.Колонка1 КАК к1,
ИсходныеДанные.Колонка2 КАК к2,
ВЫБОР
КОГДА ЕСТЬNULL(ИсходныеДанные.Колонка1, 0) = 0
ТОГДА 0
ИНАЧЕ ВЫРАЗИТЬ(ЕСТЬNULL(ИсходныеДанные.Колонка2, 0) / ИсходныеДанные.Колонка1 * 100 КАК ЧИСЛО(15, 2))
КОНЕЦ КАК ПроцентноеОтношение
ИЗ
&ТаблицаИсточника КАК ИсходныеДанные
Разберем этот программный код по шагам, чтобы понять логику его работы:
ВЫБОР...КОГДА...ТОГДА...ИНАЧЕ...КОНЕЦ представляет собой аналог условного оператора в языках программирования. Она позволяет СУБД проверять условия последовательно.ЕСТЬNULL(ИсходныеДанные.Колонка1, 0) проверяет значение в первой колонке. Если там содержится NULL, функция заменяет его на числовой ноль. Если там уже содержится число (включая реальный 0), оно остается без изменений.КОГДА ЕСТЬNULL(ИсходныеДанные.Колонка1, 0) = 0 проверяет, не равен ли наш знаменатель нулю (или NULL, который мы предварительно превратили в ноль). Если это условие выполняется, система сразу же возвращает результат 0 в качестве процента, минуя математическую операцию деления. Тем самым мы гарантируем абсолютную защиту от ошибки деления на ноль.ИНАЧЕ происходит непосредственный расчет. Мы берем значение числителя ЕСТЬNULL(ИсходныеДанные.Колонка2, 0), делим на значение знаменателя Колонка1 (в этой ветке мы уже на 100% уверены, что знаменатель не равен нулю) и умножаем на 100 для получения процентного значения.ВЫРАЗИТЬ(... КАК ЧИСЛО(15, 2)) используется для приведения типа результата. При делении чисел в базах данных часто получаются бесконечные периодические дроби или числа с огромным количеством знаков после запятой (например, 33.33333333333...). С помощью этой функции мы жестко округляем результат до двух знаков после запятой, что делает вывод данных эстетичным и читаемым для пользователя.Хотя расчет процентов внутри запроса (как описано в Решении 1) является абсолютно рабочим и часто применяется на практике, опытные разработчики предпочитают использовать другой, более гибкий и архитектурно правильный подход при разработке отчетов на СКД (Системе компоновки данных).
Проанализируем ситуацию: почему рассчитывать проценты (умножая на 100) прямо в запросе не всегда полезно? Дело в том, что если мы получим готовые проценты в запросе, то при расчете общих итогов по группировкам (например, по иерархии номенклатуры или по контрагентам) СКД попытается просто сложить эти проценты или рассчитать их среднее арифметическое. С точки зрения математики, сумма процентов или среднее значение от процентов по разным строкам — это грубейшая ошибка. Итоговый процент должен рассчитываться заново как отношение суммы всех числителей к сумме всех знаменателей.
Поэтому правильный подход заключается в следующем: в самом запросе мы рассчитываем только «чистый» коэффициент отношения (без умножения на 100), а форматирование и отображение в виде процентов перекладываем на уровень представления СКД.
Разберем этот метод по шагам:
Шаг 1. В запросе СКД пишем расчет коэффициента:
ВЫБРАТЬ
ИсходныеДанные.Колонка1 КАК к1,
ИсходныеДанные.Колонка2 КАК к2,
ВЫБОР
КОГДА ЕСТЬNULL(ИсходныеДанные.Колонка1, 0) = 0
ТОГДА 0
ИНАЧЕ ИсходныеДанные.Колонка2 / ИсходныеДанные.Колонка1
КОНЕЦ КАК КоэффициентОтношения
ИЗ
&ТаблицаИсточника КАК ИсходныеДанные
Обратите внимание: мы не умножаем результат на 100. Для строки с исходными данными 1000 и 630 результатом будет число 0.63, а для строки 1200 и 420 — число 0.35.
Шаг 2. Переходим на вкладку «Наборы данных» в конструкторе схемы компоновки данных. Находим наше поле КоэффициентОтношения.
Шаг 3. В колонке «Оформление» для данного поля нажимаем на кнопку выбора. В открывшемся окне параметров оформления находим свойство «Формат».
Шаг 4. В окне конструктора форматной строки настраиваем следующие параметры:
ЧДЦ) равным 2.ЧС) равным 100. Этот параметр автоматически умножает выводимое число на 100 при отображении на экране, сохраняя в самой базе данных исходное дробное число.ЧФ) пишем знак процента в кавычках: '%'.В результате сформированная форматная строка будет выглядеть так:
ЧДЦ=2; ЧС=100; ЧФ='#'%
При таком подходе СКД сама умножит значение 0.35 на 100, округлит до двух знаков после запятой и выведет на экран красивое значение 35,00 %. При этом в итогах по группировкам отчета СКД сможет корректно рассчитать процент, если в поле «Ресурсы» мы укажем правильную формулу расчета общего итога:
СУММА(к2) / ВЫБОР КОГДА СУММА(к1) = 0 ТОГДА 1 ИНАЧЕ СУММА(к1) КОНЕЦ
В оригинальном обсуждении на форуме участники не упустили возможность поиронизировать над математической опечаткой автора, упомянув известные фамилии авторов школьных учебников. Для понимания атмосферы профессионального сообщества разберем этот контекст:
Ирония коллег помогла автору темы быстро осознать свою ошибку, совершенную из-за банального переутомления после выходных дней. Это лишний раз доказывает пользу здорового отдыха для сохранения концентрации внимания при написании программного кода.
Подводя итоги, сформулируем основные правила, которыми должен руководствоваться разработчик 1С при выводе процентных показателей в отчетах:
ВЫБОР.ЕСТЬNULL для замены неопределенных значений базы данных на числовые нули во избежание пустых полей в отчетах.ВЫРАЗИТЬ.