В процессе разработки в системе 1С:Предприятие часто возникает задача частичного обновления данных в табличной части (ТЧ) документа на основе внешней таблицы значений (ТЗ). Например, когда мы получаем дополнительные сведения о сотрудниках из внешней базы (такой как ЗУП) и хотим обновить только конкретную колонку (например, ОстатокДней), не затрагивая остальные 20 реквизитов строки.
Рассмотрим несколько способов решения этой задачи: от простого перебора до профессиональных методов оптимизации, которые обеспечат высокую производительность системы.
Это классический и наиболее понятный метод. Его суть заключается в том, что мы обходим каждую строку нашей таблицы-источника (ТЗ) и ищем соответствующие ей строки в табличной части документа по ключевому полю (в нашем случае — Сотрудник). Проанализируем реализацию этого метода.
Для оптимизации поиска важно инициализировать объект Структура один раз вне тела цикла, а внутри цикла лишь менять значение его свойств. Это избавляет систему от лишних операций по созданию новых объектов в памяти на каждой итерации.
// Предположим, ТабОбщ — это наша ТЗ с данными, а Объект.Состав — ТЧ документа
СтруктураПоиска = Новый Структура("Сотрудник");
Для каждого СтрИсточник из ТабОбщ Цикл
// Устанавливаем ключ поиска
СтруктураПоиска.Сотрудник = СтрИсточник.Сотрудник;
// Ищем все подходящие строки в ТЧ документа
МассивНайденныхСтрок = Объект.Состав.НайтиСтроки(СтруктураПоиска);
// Обязательно проверяем, найдены ли строки, чтобы избежать ошибок обращения к индексу [0]
Для каждого СтрокаТЧ Из МассивНайденныхСтрок Цикл
СтрокаТЧ.ОстатокДней = СтрИсточник.СсылкаДни;
КонецЦикла;
КонецЦикла;
Обратите внимание, что мы используем вложенный цикл Для каждого СтрокаТЧ Из МассивНайденныхСтрок. Это критически важно, так как в табличной части документа может быть несколько строк с одним и тем же сотрудником. Если использовать просто МассивНайденныхСтрок[0], данные обновятся только в первой найденной строке, что может привести к логическим ошибкам в учете.
Если таблица значений или документ содержат тысячи строк, метод НайтиСтроки может работать медленно, так как он выполняет поиск перебором для каждого элемента. Выясним, как ускорить этот процесс с помощью объекта Соответствие.
Этот подход работает быстрее, потому что поиск в Соответствие осуществляется по хеш-таблице за константное время. Разберем алгоритм:
Соответствие.
// 1. Подготовка данных в соответствии
ДанныеДляЗаполнения = Новый Соответствие;
Для каждого СтрТЗ Из ТабОбщ Цикл
ДанныеДляЗаполнения.Вставить(СтрТЗ.Сотрудник, СтрТЗ.СсылкаДни);
КонецЦикла;
// 2. Обход ТЧ документа (всего один проход)
Для каждого СтрокаТЧ Из Объект.Состав Цикл
ЗначениеДней = ДанныеДляЗаполнения.Получить(СтрокаТЧ.Сотрудник);
// Если значение найдено в соответствии, обновляем колонку
Если ЗначениеДней <> Неопределено Тогда
СтрокаТЧ.ОстатокДней = ЗначениеДней;
КонецЕсли;
КонецЦикла;
Важный нюанс: использование соответствия позволяет избежать вложенных циклов и значительно сократить время выполнения кода на больших объемах данных.
Если вы все же предпочитаете использовать НайтиСтроки, но данных много, не забудьте про индексацию. В 1С объекты типа ТаблицаЗначений поддерживают создание индексов для ускорения поиска.
Перед началом поиска по таблице выполните команду: ТабОбщ.Индексы.Добавить("Сотрудник");. Это создаст внутренний индекс, и метод НайтиСтроки будет отрабатывать мгновенно. Однако помните, что для самих табличных частей (объектов типа ДанныеФормыКоллекция или ТЧ документа) программное создание индексов в рантайме недоступно, поэтому поиск "по документу" (как в способе 1) индексацию ТЧ использовать не будет.
В ситуациях, когда имен колонок в ТЗ и ТЧ много и они совпадают (например, нужно перенести не только ОстатокДней, но и Подразделение, Должность и т.д.), вместо ручного присваивания каждой колонки целесообразно использовать метод ЗаполнитьЗначенияСвойств.
Проанализируем пример совмещения поиска и группового заполнения реквизитов:
СтруктураПоиска = Новый Структура("Сотрудник");
Для каждого СтрИсточник из ТабОбщ Цикл
СтруктураПоиска.Сотрудник = СтрИсточник.Сотрудник;
МассивСтрок = Объект.Состав.НайтиСтроки(СтруктураПоиска);
Для каждого СтрокаПриемник Из МассивСтрок Цикл
// Метод сам сопоставит имена колонок ТЗ и ТЧ и перенесет данные
ЗаполнитьЗначенияСвойств(СтрокаПриемник, СтрИсточник);
КонецЦикла;
КонецЦикла;
Это делает код более читаемым и устойчивым к изменениям структуры метаданных. Если в будущем в таблицу добавятся новые колонки с такими же именами, они начнут переноситься автоматически.
Как было отмечено в обсуждении, часто данные сопоставляются по уникальному идентификатору (УИД). При работе с методом ПолучитьСсылку(Новый УникальныйИдентификатор(СтрокаУИД)) будьте осторожны: если объекта с таким идентификатором в вашей базе нет, 1С создаст «пустую» или «битую» ссылку (Объект не найден).
Перед тем как использовать найденную по УИД ссылку в документе, рекомендуется проверить её наличие в базе с помощью метода Метаданные.НайтиПоПолномуИмени(...).ПустаяСсылка() или попыткой получения объекта. Если ваша база «самописная», как у автора темы, надежнее всего организовать поиск по дополнительному реквизиту, где хранится ИдентификаторЗУП, используя метод Справочники.Посетители.НайтиПоРеквизиту("ID_ZUP", ЗначениеИзТЗ).
Мы рассмотрели различные подходы к заполнению табличной части. Для небольших документов вполне подходит стандартный поиск через НайтиСтроки. Для высоконагруженных систем и больших таблиц лучшим выбором будет использование Соответствия. Не забывайте обрабатывать ситуации с дублями строк и всегда проверяйте корректность найденных ссылок при обмене данными между разными конфигурациями 1С.