В практике разработки на платформе 1С:Предприятие 8 часто возникает задача построения отчетов по дебиторской задолженности в разрезе интервалов (так называемое «старение» задолженности). Одной из ключевых сложностей здесь является расчет количества дней, прошедших с момента возникновения долга или планируемой даты его погашения. Проанализируем типичную ситуацию, когда в запросе требуется проверить наличие специализированной даты в документе и, если она не заполнена, использовать стандартную дату документа.
Рассмотрим ситуацию: автор запроса пытается вычислить сумму оборота по взаиморасчетам, которая попадает в определенный временной интервал (например, от 8 до 14 дней). При этом логика выбора даты для расчета РАЗНОСТЬДАТ довольно громоздкая. Проблема усугубляется наличием избыточных условий ВЫБОР и потенциальными ошибками при работе с документами, у которых реквизит ДатаПогашениеЗадолженности отсутствует или равен NULL.
Первоначальный вариант кода содержал избыточные проверки:
ВЫБОР
КОГДА ВзаиморасчетыСКонтрагентамиОбороты.Регистратор ССЫЛКА Документ.ПлатежноеПоручениеВходящее
ТОГДА ВзаиморасчетыСКонтрагентамиОбороты.СуммаВзаиморасчетовРасход
ИНАЧЕ ВзаиморасчетыСКонтрагентамиОбороты.СуммаВзаиморасчетовРасход
КОНЕЦ
Как мы видим, в обеих ветках условия ТОГДА и ИНАЧЕ выполняется одно и то же действие. Это не только замедляет чтение кода, но и может запутать при дальнейшей поддержке. Выясним, как сделать этот запрос эффективнее.
Вместо того чтобы писать два отдельных условия для проверки нижней и верхней границы интервала ( > 7 и <= 14), в языке запросов 1С рекомендуется использовать оператор МЕЖДУ. Это делает код лаконичным и более понятным для СУБД.
Проанализируем обновленную конструкцию:
ВЫБОР
КОГДА РАЗНОСТЬДАТ(ВЫБОР
КОГДА ВзаиморасчетыСКонтрагентамиОбороты.Регистратор.ДатаПогашениеЗадолженности > ДАТАВРЕМЯ(1, 1, 1)
ТОГДА ВзаиморасчетыСКонтрагентамиОбороты.Регистратор.ДатаПогашениеЗадолженности
ИНАЧЕ ВзаиморасчетыСКонтрагентамиОбороты.Регистратор.Дата
КОНЕЦ, &КонецПериода, ДЕНЬ) МЕЖДУ 8 И 14
ТОГДА ВзаиморасчетыСКонтрагентамиОбороты.СуммаВзаиморасчетовОборот
ИНАЧЕ 0
КОНЕЦ
Важный момент: использование МЕЖДУ 8 И 14 эквивалентно условию >= 8 И <= 14. Обратите внимание, что границы интервала включаются в расчет.
При обращении к реквизитам через Регистратор, который имеет составной тип, мы можем столкнуться с тем, что у некоторых видов документов (например, у платежных поручений) просто нет реквизита ДатаПогашениеЗадолженности. В этом случае система вернет NULL.
Рассмотрим, как правильно обрабатывать такие ситуации:
ДАТАВРЕМЯ(1, 1, 1, 0, 0, 0).ЕСТЬNULL(Поле, ЗначениеЗамены).Однако, так как нам нужно проверить и на NULL, и на "пустоту", конструкция с ВЫБОР часто остается наиболее надежной, но ее можно упростить, проверяя только положительное условие (заполнена ли дата), как показано в примере выше.
Часто программисты сталкиваются с тем, что при расчете сумм в запросе по сложным условиям данные начинают "размножаться" или выводиться отдельными строками. Если в консоли запросов вы видите несколько строк там, где должна быть одна общая сумма, значит, в запросе отсутствуют группировки или они настроены неверно.
Рассмотрим правила корректного суммирования:
СУММА), все остальные поля в списке выбора должны быть перечислены в секции СГРУППИРОВАТЬ ПО.СУММА(ИмяВашегоПоля).Обороты может быть избыточным, если вам нужны только остатки долга на конкретную дату.Хотя предложенные выше варианты решают задачу функционально, с точки зрения производительности обращение к полям через точку от Регистратора (Регистратор.ДатаПогашениеЗадолженности) является "тяжелой" операцией. Это происходит потому, что Регистратор — поле составного типа, и СУБД приходится выполнять соединения со всеми возможными таблицами документов.
Для оптимизации проанализируем следующие шаги:
ВЫРАЗИТЬ(Регистратор КАК Документ.РеализацияТоваровУслуг).ДатаПогашениеЗадолженности.РАЗНОСТЬДАТ рекомендуется использовать НАЧАЛОПЕРИОДА, чтобы время документа не влияло на расчет количества дней.Итоговый, структурно правильный подход к расчету суммы в интервале в рамках запроса будет выглядеть так:
ВЫБРАТЬ
Взаиморасчеты.Контрагент КАК Контрагент,
СУММА(ВЫБОР
КОГДА РАЗНОСТЬДАТ(
ЕСТЬNULL(
ВЫБОР
КОГДА Взаиморасчеты.Регистратор.ДатаПогашениеЗадолженности = ДАТАВРЕМЯ(1, 1, 1)
ТОГДА NULL
ИНАЧЕ Взаиморасчеты.Регистратор.ДатаПогашениеЗадолженности
КОНЕЦ,
Взаиморасчеты.Регистратор.Дата
),
&КонецПериода,
ДЕНЬ
) МЕЖДУ 8 И 14
ТОГДА Взаиморасчеты.СуммаВзаиморасчетовОборот
ИНАЧЕ 0
КОНЕЦ) КАК Долг_8_14_Дней
ИЗ
РегистрНакопления.ВзаиморасчетыСКонтрагентами.Обороты(&НачалоПериода, &КонецПериода, Регистратор, ) КАК Взаиморасчеты
СГРУППИРОВАТЬ ПО
Взаиморасчеты.Контрагент
Этот подход позволяет эффективно распределить суммы по нужным "корзинам" (интервалам) прямо в момент выполнения запроса, минимизируя нагрузку на последующую обработку данных в коде или в отчете СКД.