Сравнение данных между двумя информационными базами — задача, с которой часто сталкиваются разработчики при аудите миграций, проверке корректности обменов или сверке бухгалтерских проводок. Основная проблема здесь заключается в производительности: если данных много, стандартные подходы могут выполняться часами. Разберем на примере реальной ситуации, как оптимизировать алгоритм сверки проводок в разрезе регистраторов и почему классический "запрос в цикле" является худшим решением.
Рассмотрим стандартную ситуацию: у нас есть список регистраторов (документов), и нам нужно проверить их движения в текущей базе и во внешней базе (например, через COM-соединение или HTTP-сервис). Первый интуитивный порыв программиста — перебрать список документов и для каждого выполнить запрос к базе данных.
Проанализируем, что происходит на техническом уровне при использовании метода Запрос.Выполнить() внутри цикла:
Как показывает практика, при обработке списка документов таким методом время выполнения может составлять 20 минут и более, что неприемлемо для оперативной работы.
Вместо множества мелких запросов гораздо эффективнее выполнить один массивный запрос, который выберет все необходимые данные сразу по всему списку регистраторов. Полученные данные выгружаются в ТаблицаЗначений.
Важный нюанс: Чтобы поиск по этой таблице работал быстро, обязательно нужно использовать индексы. Без индексации метод НайтиСтроки() будет выполнять полный перебор таблицы для каждой записи, что сведет на нет всю выгоду от одного запроса.
Рассмотрим пример кода для подготовки такой таблицы:
// Выбираем данные одним запросом
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ХозрасчетныйДвиженияССубконто.Регистратор КАК Регистратор,
| ХозрасчетныйДвиженияССубконто.СчетДт КАК СчетДт,
| ХозрасчетныйДвиженияССубконто.Сумма КАК Сумма
|ИЗ
| РегистрБухгалтерии.Хозрасчетный.ДвиженияССубконто( , , Регистратор В (&СписокРегистраторов)) КАК ХозрасчетныйДвиженияССубконто";
Запрос.УстановитьПараметр("СписокРегистраторов", СписокДокументов);
ТаблицаПроводок = Запрос.Выполнить().Выгрузить();
// Добавляем индекс по полю, по которому будем искать
ТаблицаПроводок.Индексы.Добавить("Регистратор");
Использование этого метода позволяет сократить время обработки с 20 минут до 1 минуты, так как основная работа перекладывается на мощный движок SQL, а поиск в памяти 1С по индексу происходит мгновенно.
При очень больших объемах данных (сотни тысяч или миллионы проводок) выгрузка всей информации в одну ТаблицаЗначений может привести к чрезмерному потреблению оперативной памяти и падению рабочего процесса rphost. Выясним, как обойти это ограничение.
Оптимальным решением будет порционная обработка (батчинг). Мы разбиваем общий список регистраторов на пачки (например, по 1000 штук или по месяцам/неделям) и обрабатываем каждую пачку отдельно. Это гарантирует, что объем данных в памяти в один момент времени не превысит критического значения.
Проанализируем алгоритм гибридного метода:
Если проект требует максимальной производительности, стоит рассмотреть дополнительные возможности платформы 1С.
1. Использование временных таблиц (ВТ): Вместо передачи списка ссылок в условие В (&Список), можно загрузить этот список во временную таблицу и использовать ВНУТРЕННЕЕ СОЕДИНЕНИЕ. Это позволит SQL-серверу использовать эффективные алгоритмы типа Merge Join или Hash Match.
2. Хеширование строк: Если нужно сравнивать строки проводок по 10-15 полям (счета, субконто, количество, подразделения), сравнение каждой колонки в коде 1С будет медленным. Выгоднее при выборке в запросе сформировать "ключ" строки (например, строку-конкатенацию всех значимых полей или хеш-сумму) и сравнивать только эти ключи.
3. Отказ от объектов в пользу примитивных типов: При выгрузке в ТаблицаЗначений старайтесь не выбирать ссылки на тяжелые объекты, если это не нужно. Используйте УникальныйИдентификатор или строковое представление кода/номера. Это существенно снижает "раздувание" объектного кэша памяти.
4. Асинхронность: Для очень тяжелых задач сверку можно разделить на несколько ФоновыеЗадания. Каждое задание будет обрабатывать свой период времени или свой диапазон ГУИДов, используя всю мощность многоядерных серверных процессоров.
Выбор между запросом в цикле и таблицей значений очевиден: всегда используйте пакетную выборку в таблицу. Основные правила эффективной сверки:
Запрос.Выполнить() внутри цикла по документам.ТаблицаЗначений перед использованием методов поиска.ПланыОбмена, чтобы проверять только измененные данные, а не всю базу целиком.Соблюдение этих принципов позволит вам создавать быстрые и надежные инструменты для анализа данных, экономя время пользователей и ресурсы сервера.