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