Задача поиска набора записей в регистре или таблице, сумма которых в точности соответствует целевому значению, является классической задачей компьютерных наук, известной как «Задача о сумме подмножества». В рамках разработки на платформе 1С мы часто сталкиваемся с необходимостью сверки оплат с накладными или поиска комбинаций проводок. Давайте разберем, как подходить к решению этой задачи, учитывая возможности 1С и ограничения SQL-запросов.
Если ваша бизнес-задача сводится к поиску всего двух строк, сумма которых дает искомый результат, мы можем применить метод декартового произведения. Суть метода заключается в соединении таблицы самой с собой. Рассмотрим этот подход по шагам:
Посмотрим, как это выглядит в коде запроса:
ВЫБРАТЬ
Т1.Сумма КАК Сумма1,
Т2.Сумма КАК Сумма2,
Т1.Сумма + Т2.Сумма КАК Итого
ПОМЕСТИТЬ ВТ_Данные
ИЗ РегистрНакопления.ВашРегистр КАК Т1
ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ВашРегистр КАК Т2
ПО Т1.Период = Т2.Период // Ограничение, если нужно
ГДЕ
Т1.Сумма + Т2.Сумма = &ИскомаяСумма
И Т1.Ссылка <> Т2.Ссылка;
Важно: Данный метод создает декартово произведение. Если в таблице 1000 записей, запрос может обработать до миллиона комбинаций, что критически скажется на производительности. Применяйте этот метод только для небольших выборок.
Необходимо осознавать, что запросы в 1С (и SQL в целом) плохо приспособлены для перебора всех возможных комбинаций. Если количество слагаемых заранее неизвестно (например, сумма может складываться из 3, 5 или 10 строк), построить универсальный запрос практически невозможно. Попытка создать множество соединений таблицы самой с собой приведет к экспоненциальному росту нагрузки и, вероятнее всего, к ошибке нехватки памяти на сервере.
Для решения задачи, где количество слагаемых не ограничено двумя строками, мы рекомендуем использовать алгоритм «с возвратом» (Backtracking) на встроенном языке 1С. Этот метод позволяет эффективно управлять процессом поиска.
Рассмотрим логику работы алгоритма:
Массив или ТаблицаЗначений.Проанализируем пример логики функции:
Процедура НайтиКомбинацию(СписокЧисел, ЦелеваяСумма, ТекущийИндекс, ТекущаяСумма, ТекущийПуть)
Если ТекущаяСумма = ЦелеваяСумма Тогда
// Решение найдено, обрабатываем результат
Возврат;
КонецЕсли;
Для Индекс = ТекущийИндекс По СписокЧисел.Количество() - 1 Цикл
Число = СписокЧисел[Индекс];
Если ТекущаяСумма + Число <= ЦелеваяСумма Тогда
ТекущийПуть.Добавить(Число);
НайтиКомбинацию(СписокЧисел, ЦелеваяСумма, Индекс + 1, ТекущаяСумма + Число, ТекущийПуть);
ТекущийПуть.Удалить(ТекущийПуть.Количество() - 1); // Откат
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Чтобы алгоритм работал быстро, воспользуемся следующими рекомендациями:
ТаблицаЗначений один раз, а не обращайтесь к регистру внутри циклов.В завершение подчеркнем: универсального «волшебного» запроса для этой задачи не существует. Наиболее надежный путь — это подготовка данных запросом и последующая обработка этой выборки алгоритмом на встроенном языке 1С. Такой подход обеспечит стабильность работы вашей системы и предсказуемую производительность.