В практике разработки на платформе 1С:Предприятие 8.3 часто возникает задача подбора документов «по очереди» до тех пор, пока сумма их показателей не достигнет или не превысит определенный порог. Это классическая задача на накопленный итог (running total). Несмотря на то, что такие задачи кажутся простыми для циклического перебора в коде, их реализация на уровне запроса позволяет значительно повысить производительность системы при работе с большими массивами данных.
В данной статье мы подробно разберем, как с помощью языка запросов 1С определить минимальный перечень документов реализации, начиная с определенной точки, общая сумма которых превышает заданное число.
Если ваша конфигурация работает на версии платформы ниже 8.3.20, стандартным способом решения будет использование левого или внутреннего соединения таблицы самой с собой. Проанализируем логику этого подхода: мы берем таблицу документов (Т1) и для каждой её строки присоединяем все те же документы (Т2), которые были созданы «ранее или одновременно» с текущим.
Рассмотрим пример запроса, где мы рассчитываем нарастающий итог по количеству продукции:
ВЫБРАТЬ
Т1.Ссылка КАК Документ,
Т1.Дата КАК ДатаДокумента,
СУММА(Т2.Количество) КАК СуммаНарастающимИтогом
ИЗ
Документ.РеализацияТоваровУслуг КАК Т1
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Документ.РеализацияТоваровУслуг КАК Т2
ПО Т1.Дата >= Т2.Дата
И Т2.Дата >= &ДатаНачала
ГДЕ
Т1.Дата >= &ДатаНачала
СГРУППИРОВАТЬ ПО
Т1.Ссылка,
Т1.Дата
ИМЕЮЩИЕ
СУММА(Т2.Количество) >= &ТребуемаяСумма
Разберем, что происходит в этом запросе. В блоке ПО мы указываем условие, которое заставляет систему для каждой записи Т1 найти все записи Т2, предшествующие ей по времени. Затем мы группируем результат по документу из Т1 и суммируем показатели из Т2. Условие ИМЕЮЩИЕ (HAVING) позволяет отсечь те строки, где накопленная сумма еще не достигла целевого значения.
При использовании только поля Дата в условии соединения мы можем столкнуться с проблемой: если два документа созданы в одну и ту же секунду, 1С не сможет однозначно определить их порядок. Это приведет к неверному расчету итога (суммы будут дублироваться для обоих документов).
Чтобы избежать этой ситуации, мы должны использовать составное условие, включающее уникальный идентификатор — Ссылка. Проанализируем правильное условие соединения:
Условие: Т1.Дата > Т2.Дата ИЛИ (Т1.Дата = Т2.Дата И Т1.Ссылка >= Т2.Ссылка)
Такой подход гарантирует строгую последовательность документов. В запросе это будет выглядеть следующим образом:
ВЫБРАТЬ
Т1.Ссылка КАК Ссылка,
СУММА(Т2.СуммаДокумента) КАК Накопительно
ИЗ
Документ.РеализацияТоваровУслуг КАК Т1
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Документ.РеализацияТоваровУслуг КАК Т2
ПО (Т1.Дата > Т2.Дата ИЛИ (Т1.Дата = Т2.Дата И Т1.Ссылка >= Т2.Ссылка))
И Т2.Ссылка В (&МассивДокументовДляАнализа)
ГДЕ
Т1.Ссылка В (&МассивДокументовДляАнализа)
СГРУППИРОВАТЬ ПО
Т1.Ссылка
Современные версии платформы 1С поддерживают оконные функции. Это наиболее эффективный и лаконичный способ расчета накопленного итога. Выясним причину его превосходства: СУБД (SQL Server, PostgreSQL) выполняет такой расчет за один проход по таблице, не создавая огромных временных соединений в памяти.
Синтаксис 1С для оконной функции выглядит так:
ВЫБРАТЬ
Т.Ссылка КАК Документ,
Т.СуммаДокумента КАК ТекущаяСумма,
СУММА(Т.СуммаДокумента) ОБРАБОТКА (УПОРЯДОЧИТЬ ПО Т.Дата, Т.Ссылка) КАК НарастающийИтог
ИЗ
Документ.РеализацияТоваровУслуг КАК Т
ГДЕ
Т.Дата >= &ДатаНачала
Здесь ключевым является ключевое слово ОБРАБОТКА, которое указывает системе на необходимость выполнения агрегатной функции СУММА внутри «окна», упорядоченного по дате и ссылке. После выполнения этого запроса нам останется лишь выбрать строки, где НарастающийИтог меньше или равен нашей цели, плюс первая строка, которая её превысила.
По условию задачи нам нужно не просто посчитать итоги, а выбрать конкретные документы. Рассмотрим алгоритм действий по шагам:
>= &ЗаданноеЧисло.Этот двухэтапный подход позволяет гарантированно включить в результат и те документы, которые были «до», и тот самый последний документ, который «замкнул» требуемую сумму.
При реализации накопленного итога в запросах крайне важно помнить о следующих моментах:
ИНДЕКСИРОВАТЬ ПО для временных таблиц по полям, участвующим в соединении (Дата, Ссылка). Без индексов соединение таблицы «самой на себя» на 1000 строк превратится в проверку 1 000 000 комбинаций, что может замедлить сервер.ВычислитьВыражение, что работает быстрее и надежнее кастомных SQL-запросов.Таким образом, мы проанализировали ситуацию и выяснили, что наиболее стабильным решением для старых версий является тета-соединение по дате и ссылке, а для новых — использование встроенных оконных функций. Выбор метода зависит от версии вашей платформы и требований к читаемости кода.