Как правильно разделить показатель на функцию РАЗНОСТЬДАТ в запросе 1С

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При построении аналитических отчетов и расчете средних показателей (например, среднемесячного расхода, среднедневных продаж или оборачиваемости) разработчики часто сталкиваются с необходимостью деления агрегированных сумм на интервал времени. Однако попытка разделить числовое поле напрямую на выражение с функцией РАЗНОСТЬДАТ нередко приводит к ошибкам выполнения запроса или некорректным результатам. Разберем по шагам, почему возникают эти ошибки и как правильно составить запрос.

Анализ типичных ошибок при делении на разность дат

Рассмотрим выражение, вызывающее ошибку:


ТоварыНаСкладахОстаткиИОбороты.КоличествоРасход / КОЛИЧЕСТВО(РАЗНОСТЬДАТ(&ДатаНач, КОНЕЦПЕРИОДА(&ДатаКон, ДЕНЬ), МЕСЯЦ))

В приведенной конструкции допущены сразу две принципиальные ошибки:

  1. Ошибочное использование агрегатной функции КОЛИЧЕСТВО. В языке запросов 1С функция КОЛИЧЕСТВО(...) является аналогом SQL-агрегата COUNT. Она предназначена для подсчета числа строк в выборке или группировке. Оборачивать в нее скалярное вычисление разности дат между двумя параметрами бессмысленно и синтаксически неверно.
  2. Деление на ноль из-за особенностей работы РАЗНОСТЬДАТ. Функция РАЗНОСТЬДАТ подсчитывает не фактическую временную продолжительность, а количество переходов через границы указанного периода. Если дата начала и дата окончания находятся внутри одного календарного месяца (например, с 01.03.2024 по 31.03.2024), функция РАЗНОСТЬДАТ(&ДатаНач, &ДатаКон, МЕСЯЦ) вернет 0. При попытке деления СУБД генерирует исключение «Деление на ноль».

Вариант 1. Корректный расчет среднемесячного показателя

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

Первый способ — инкремент количества месяцев, если границы отчета всегда задаются от начала первого месяца до конца последнего:


ВЫБРАТЬ
    ТоварыНаСкладахОстаткиИОбороты.Номенклатура КАК Номенклатура,
    СУММА(ТоварыНаСкладахОстаткиИОбороты.КоличествоРасход) КАК КоличествоРасход,
    СУММА(ТоварыНаСкладахОстаткиИОбороты.КоличествоРасход) / (РАЗНОСТЬДАТ(&ДатаНач, &ДатаКон, МЕСЯЦ) + 1) КАК СреднееВМесяц
ИЗ
    РегистрНакопления.ТоварыНаСкладах.ОстаткиИОбороты(&ДатаНач, &ДатаКон, , , ) КАК ТоварыНаСкладахОстаткиИОбороты
СГРУППИРОВАТЬ ПО
    ТоварыНаСкладахОстаткиИОбороты.Номенклатура

Второй способ — явная проверка через конструкцию ВЫБОР КОГДА. Этот подход универсален и предотвращает деление на ноль при любых переданных параметрах дат:


ВЫБОР
    КОГДА РАЗНОСТЬДАТ(&ДатаНач, &ДатаКон, МЕСЯЦ) = 0
        ТОГДА СУММА(ТоварыНаСкладахОстаткиИОбороты.КоличествоРасход)
    ИНАЧЕ СУММА(ТоварыНаСкладахОстаткиИОбороты.КоличествоРасход) / (РАЗНОСТЬДАТ(&ДатаНач, &ДатаКон, МЕСЯЦ) + 1)
КОНЕЦ КАК СреднееВМесяц

Вариант 2. Расчет через количество дней

Если отчет формируется за произвольный период, не кратный целому месяцу (например, за 10 дней или полтора месяца), вычисление разности в месяцах дает существенную погрешность. В таких задачах надежнее считать интервал в днях:


ВЫБРАТЬ
    ТоварыНаСкладах.Номенклатура КАК Номенклатура,
    СУММА(ТоварыНаСкладах.КоличествоРасход) КАК КоличествоРасход,
    // Среднедневной расход
    СУММА(ТоварыНаСкладах.КоличествоРасход) / (РАЗНОСТЬДАТ(&ДатаНач, КОНЕЦПЕРИОДА(&ДатаКон, ДЕНЬ), ДЕНЬ) + 1) КАК СреднееВДень,
    // Приведение к среднемесячному расходу (через усредненный коэффициент 30.4375 или 30)
    (СУММА(ТоварыНаСкладах.КоличествоРасход) / (РАЗНОСТЬДАТ(&ДатаНач, КОНЕЦПЕРИОДА(&ДатаКон, ДЕНЬ), ДЕНЬ) + 1)) * 30.4375 КАК СреднемесячныйРасход
ИЗ
    РегистрНакопления.ТоварыНаСкладах.ОстаткиИОбороты(&ДатаНач, &ДатаКон, , , ) КАК ТоварыНаСкладах
СГРУППИРОВАТЬ ПО
    ТоварыНаСкладах.Номенклатура

Важные правила построения запроса с группировками и соединениями

Проанализируем сопутствующие моменты, на которые важно обращать внимание при написании подобных запросов:

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