Как оптимизировать запрос 1С для расчета сроков погашения задолженности по документам?

Программист 1С v8.3 (Обычные формы) Управленческий учет Торговля и дистрибуция
← На главную

В практике разработки на платформе 1С:Предприятие 8 часто возникает задача построения отчетов по дебиторской задолженности в разрезе интервалов (так называемое «старение» задолженности). Одной из ключевых сложностей здесь является расчет количества дней, прошедших с момента возникновения долга или планируемой даты его погашения. Проанализируем типичную ситуацию, когда в запросе требуется проверить наличие специализированной даты в документе и, если она не заполнена, использовать стандартную дату документа.

Разбор исходной проблемы

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

Первоначальный вариант кода содержал избыточные проверки:


ВЫБОР
    КОГДА ВзаиморасчетыСКонтрагентамиОбороты.Регистратор ССЫЛКА Документ.ПлатежноеПоручениеВходящее
        ТОГДА ВзаиморасчетыСКонтрагентамиОбороты.СуммаВзаиморасчетовРасход
    ИНАЧЕ ВзаиморасчетыСКонтрагентамиОбороты.СуммаВзаиморасчетовРасход
КОНЕЦ

Как мы видим, в обеих ветках условия ТОГДА и ИНАЧЕ выполняется одно и то же действие. Это не только замедляет чтение кода, но и может запутать при дальнейшей поддержке. Выясним, как сделать этот запрос эффективнее.

Оптимизация условий с использованием оператора МЕЖДУ

Вместо того чтобы писать два отдельных условия для проверки нижней и верхней границы интервала ( > 7 и <= 14), в языке запросов 1С рекомендуется использовать оператор МЕЖДУ. Это делает код лаконичным и более понятным для СУБД.

Проанализируем обновленную конструкцию:


ВЫБОР
    КОГДА РАЗНОСТЬДАТ(ВЫБОР
            КОГДА ВзаиморасчетыСКонтрагентамиОбороты.Регистратор.ДатаПогашениеЗадолженности > ДАТАВРЕМЯ(1, 1, 1)
                ТОГДА ВзаиморасчетыСКонтрагентамиОбороты.Регистратор.ДатаПогашениеЗадолженности
            ИНАЧЕ ВзаиморасчетыСКонтрагентамиОбороты.Регистратор.Дата
        КОНЕЦ, &КонецПериода, ДЕНЬ) МЕЖДУ 8 И 14 
    ТОГДА ВзаиморасчетыСКонтрагентамиОбороты.СуммаВзаиморасчетовОборот
    ИНАЧЕ 0
КОНЕЦ

Важный момент: использование МЕЖДУ 8 И 14 эквивалентно условию >= 8 И <= 14. Обратите внимание, что границы интервала включаются в расчет.

Обработка пустых значений и неопределенности

При обращении к реквизитам через Регистратор, который имеет составной тип, мы можем столкнуться с тем, что у некоторых видов документов (например, у платежных поручений) просто нет реквизита ДатаПогашениеЗадолженности. В этом случае система вернет NULL.

Рассмотрим, как правильно обрабатывать такие ситуации:

  1. Проверка на пустую дату: В 1С пустая дата — это ДАТАВРЕМЯ(1, 1, 1, 0, 0, 0).
  2. Проверка на NULL: Возникает, если реквизит не существует в таблице данного типа документа или при левом соединении.
  3. Функция ЕСТЬNULL: Позволяет сократить код. Например, ЕСТЬNULL(Поле, ЗначениеЗамены).

Однако, так как нам нужно проверить и на NULL, и на "пустоту", конструкция с ВЫБОР часто остается наиболее надежной, но ее можно упростить, проверяя только положительное условие (заполнена ли дата), как показано в примере выше.

Проблема дублирования строк и группировка данных

Часто программисты сталкиваются с тем, что при расчете сумм в запросе по сложным условиям данные начинают "размножаться" или выводиться отдельными строками. Если в консоли запросов вы видите несколько строк там, где должна быть одна общая сумма, значит, в запросе отсутствуют группировки или они настроены неверно.

Рассмотрим правила корректного суммирования:

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

Рекомендации по повышению производительности

Хотя предложенные выше варианты решают задачу функционально, с точки зрения производительности обращение к полям через точку от Регистратора (Регистратор.ДатаПогашениеЗадолженности) является "тяжелой" операцией. Это происходит потому, что Регистратор — поле составного типа, и СУБД приходится выполнять соединения со всеми возможными таблицами документов.

Для оптимизации проанализируем следующие шаги:

Итоговый, структурно правильный подход к расчету суммы в интервале в рамках запроса будет выглядеть так:


ВЫБРАТЬ
    Взаиморасчеты.Контрагент КАК Контрагент,
    СУММА(ВЫБОР
            КОГДА РАЗНОСТЬДАТ(
                ЕСТЬNULL(
                    ВЫБОР 
                        КОГДА Взаиморасчеты.Регистратор.ДатаПогашениеЗадолженности = ДАТАВРЕМЯ(1, 1, 1) 
                        ТОГДА NULL 
                        ИНАЧЕ Взаиморасчеты.Регистратор.ДатаПогашениеЗадолженности 
                    КОНЕЦ, 
                    Взаиморасчеты.Регистратор.Дата
                ), 
                &КонецПериода, 
                ДЕНЬ
            ) МЕЖДУ 8 И 14
                ТОГДА Взаиморасчеты.СуммаВзаиморасчетовОборот
            ИНАЧЕ 0
        КОНЕЦ) КАК Долг_8_14_Дней
ИЗ
    РегистрНакопления.ВзаиморасчетыСКонтрагентами.Обороты(&НачалоПериода, &КонецПериода, Регистратор, ) КАК Взаиморасчеты

СГРУППИРОВАТЬ ПО
    Взаиморасчеты.Контрагент

Этот подход позволяет эффективно распределить суммы по нужным "корзинам" (интервалам) прямо в момент выполнения запроса, минимизируя нагрузку на последующую обработку данных в коде или в отчете СКД.

← На главную