В процессе разработки на платформе 1С программисты часто сталкиваются с задачей сопоставления данных из двух различных источников. Типичный пример: у нас есть две таблицы значений (ТаблицаЗначений), и нам нужно найти строки в первой таблице, где значение определенной колонки (например, СуммаДт) совпадает со значением в колонке второй таблицы (например, СуммаКт). Несмотря на кажущуюся простоту, существует несколько способов реализации этой задачи, каждый из которых имеет свои преимущества и недостатки в зависимости от объема данных и требований к производительности.
В этой статье мы подробно рассмотрим различные подходы — от простых циклов до использования мощных механизмов запросов и индексации. Мы разберем, как избежать типичных ошибок и какой метод выбрать в конкретной ситуации.
Этот метод является одним из самых популярных, так как он позволяет искать сразу несколько строк, соответствующих условию. Для этого используется объект Структура, в который помещаются имена колонок и искомые значения.
Разберем этот метод на примере. Допустим, мы перебираем первую таблицу и для каждой строки ищем соответствие во второй:
Для Каждого СтрокаТЗ1 Из ТЗ1 Цикл
// Создаем структуру поиска, где ключ — имя колонки в ТЗ2,
// а значение — данные из текущей строки ТЗ1
Отбор = Новый Структура("СуммаКт", СтрокаТЗ1.СуммаДт);
// Метод НайтиСтроки возвращает Массив найденных строк
НайденныеСтроки = ТЗ2.НайтиСтроки(Отбор);
Для Каждого СтрНайденая Из НайденныеСтроки Цикл
// Здесь мы обрабатываем найденные совпадения
Сообщить("Найдено совпадение для суммы: " + СтрокаТЗ1.СуммаДт);
КонецЦикла;
КонецЦикла;
Важный нюанс: Метод НайтиСтроки работает эффективно, если строк не очень много. Однако при больших объемах данных (десятки тысяч строк) производительность может упасть, если не использовать индексацию. Рассмотрим это далее.
Если вы решили использовать методы НайтиСтроки или Найти, критически важно предварительно проиндексировать колонку, по которой осуществляется поиск. Без индекса система будет выполнять полный перебор таблицы при каждом обращении к методу, что приведет к квадратичному росту времени выполнения (O(n*m)).
Проанализируем, как добавить индекс:
// Добавляем индекс по колонке СуммаКт перед началом поиска
ТЗ2.Индексы.Добавить("СуммаКт");
Для Каждого СтрокаТЗ1 Из ТЗ1 Цикл
Отбор = Новый Структура("СуммаКт", СтрокаТЗ1.СуммаДт);
НайденныеСтроки = ТЗ2.НайтиСтроки(Отбор);
// Теперь поиск внутри таблицы ТЗ2 выполняется мгновенно
КонецЦикла;
Индексация переводит поиск из разряда "медленного перебора" в разряд "быстрого доступа по ключу". Если ваши таблицы содержат более 1000 строк, использование индексов становится обязательным.
Самый простой и понятный для новичка способ — это два вложенных цикла. Мы берем строку из первой таблицы и в цикле сравниваем ее со всеми строками второй таблицы.
Рассмотрим пример кода:
Для Каждого Строка1 Из ТЗ1 Цикл
Для Каждого Строка2 Из ТЗ2 Цикл
Если Строка1.СуммаДт = Строка2.СуммаКт И Строка1.Дата = Строка2.Дата Тогда
// Условие выполнено
Сообщить("Найдено полное соответствие по дате и сумме");
КонецЕсли;
КонецЦикла;
КонецЦикла;
Почему это плохо? Если в каждой таблице по 10 000 строк, то программе придется выполнить 100 000 000 сравнений. Это крайне неэффективно. Данный метод стоит использовать только в том случае, если таблицы гарантированно маленькие или условия сравнения настолько сложные, что их невозможно описать структурой или запросом.
Это "профессиональный" метод, который считается наиболее быстрым и гибким. Платформа 1С позволяет передать таблицу значений в запрос, где можно использовать всю мощь языка SQL: соединения, группировки, сложные условия и сортировки.
Разберем по шагам, как это реализовать:
ОписаниеТипов).МенеджерВременныхТаблиц.
Запрос = Новый Запрос;
МВТ = Новый МенеджерВременныхТаблиц;
Запрос.МенеджерВременныхТаблиц = МВТ;
// Помещаем первую таблицу
Запрос.Текст = "ВЫБРАТЬ * ПОМЕСТИТЬ ВТ_ТЗ1 ИЗ &ТЗ1 КАК ТЗ1";
Запрос.УстановитьПараметр("ТЗ1", ТЗ1);
Запрос.Выполнить();
// Помещаем вторую таблицу
Запрос.Текст = "ВЫБРАТЬ * ПОМЕСТИТЬ ВТ_ТЗ2 ИЗ &ТЗ2 КАК ТЗ2";
Запрос.УстановитьПараметр("ТЗ2", ТЗ2);
Запрос.Выполнить();
// Выполняем сопоставление (Внутреннее соединение)
Запрос.Текст =
"ВЫБРАТЬ
| ВТ_ТЗ1.Дата КАК Дата,
| ВТ_ТЗ1.СуммаДт КАК Сумма,
| ВТ_ТЗ1.ПредставлениеДокумента КАК Документ
|ИЗ
| ВТ_ТЗ1 КАК ВТ_ТЗ1
| ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_ТЗ2 КАК ВТ_ТЗ2
| ПО ВТ_ТЗ1.СуммаДт = ВТ_ТЗ2.СуммаКт
| И ВТ_ТЗ1.Дата = ВТ_ТЗ2.Дата";
РезультатЗапроса = Запрос.Выполнить();
ТЗ_Итог = РезультатЗапроса.Выгрузить();
Этот метод не только самый быстрый, но и позволяет сразу получить готовую таблицу с результатами, не занимаясь ручным копированием строк в циклах.
Если вам нужно очень быстро найти значение по ключу, но вы не хотите использовать запросы, можно применить объект Соответствие. Это аналог хэш-таблицы.
Проанализируем алгоритм:
ТЗ2 и заполним соответствие, где ключом будет сумма, а значением — строка таблицы или массив строк.ТЗ1 и будем "выдергивать" данные из соответствия.
КартаСумм = Новый Соответствие;
Для Каждого Стр Из ТЗ2 Цикл
// Для простоты примера считаем суммы уникальными
КартаСумм.Вставить(Стр.СуммаКт, Стр);
КонецЦикла;
Для Каждого Стр1 Из ТЗ1 Цикл
НайденнаяСтрокаТЗ2 = КартаСумм.Получить(Стр1.СуммаДт);
Если НайденнаяСтрокаТЗ2 <> Неопределено Тогда
// Совпадение найдено мгновенно!
КонецЕсли;
КонецЦикла;
Этот метод работает со скоростью O(n) и часто является самым быстрым способом сопоставления в памяти без обращения к движку запросов.
При сравнении сумм (тип Число) помните о точности. Числа с плавающей точкой могут иметь микроскопическую разницу (например, 100.000000001 и 100.00). Если данные были получены в результате деления или других сложных расчетов, прямое сравнение через = или Структуру может не сработать.
Рекомендация: Перед помещением данных в таблицы значений или перед поиском всегда используйте функцию Окр(Сумма, 2). Это гарантирует, что ваши "копейки" совпадут корректно.
Мы рассмотрели пять способов поиска и сопоставления данных. Выясним кратко, когда какой использовать:
НайтиСтроки без лишних ухищрений.Индексы.Добавить() перед поиском.Запрос с временными таблицами.Соответствие.Выбирайте метод, исходя из объема ваших данных, и не забывайте про типизацию колонок и округление чисел.