При загрузке данных из внешних источников, например, из файла Excel, часто возникает задача: нужно добавить новые записи в табличную часть документа, но при этом избежать дублирования. Стандартный алгоритм выглядит так: для каждой строки из файла нужно проверить, существует ли уже такая запись в документе. Если да — переходим к следующей строке файла. Если же после проверки всей табличной части совпадений не найдено — добавляем новую строку. Разберем, как грамотно реализовать такую проверку в 1С.
Проанализируем ситуацию, с которой столкнулся автор. При обходе табличной части в цикле возникла сложность с тем, чтобы понять, что цикл завершился, а нужная запись так и не была найдена. Первоначальная попытка добавить строку в ветке Иначе внутри цикла — это логическая ошибка, которая приведет к многократному добавлению одной и той же записи. Рассмотрим несколько правильных подходов к решению этой задачи.
Это самый универсальный и понятный способ, который легко читается и подходит для любых, даже самых сложных, условий поиска. Логика проста: перед началом цикла мы "предполагаем", что записи нет, а если находим ее в процессе — "поднимаем флаг".
Рассмотрим по шагам:
ЗаписьНайдена, и присваиваем ей значение Ложь.Для Каждого ... Из ... для обхода строк табличной части.Истина и немедленно прерываем цикл с помощью оператора Прервать. Дальнейший поиск не имеет смысла, так как мы уже получили ответ на наш вопрос.Ложь, это означает, что цикл прошел полностью, и ни одна строка не подошла под условие. Только в этом случае мы добавляем новую запись.Посмотрим на пример кода:
// Данные для поиска, полученные, например, из файла Excel
ИскомыйВидОплаты = Справочники.ВидыОплатОрганизаций.НайтиПоНаименованию("иОДИН");
СуммаИзФайла = 1500;
// Табличная часть документа
НаборЗаписей = ОбъектДок.Оплата;
// 1. Инициализация флага
Флаг_ЗаписьНайдена = Ложь;
// 2. Обход в цикле
Для Каждого Запись Из НаборЗаписей Цикл
Если Запись.ВидОплаты = ИскомыйВидОплаты Тогда
// 3. Если запись найдена, меняем флаг и выходим
Флаг_ЗаписьНайдена = Истина;
Прервать; // Выход из цикла, так как запись уже существует
КонецЕсли;
КонецЦикла;
// 4. Проверка флага и добавление записи
Если НЕ Флаг_ЗаписьНайдена Тогда
// Запись не была найдена, значит, можно добавить новую
НоваяСтрока = НаборЗаписей.Добавить();
НоваяСтрока.ВидОплаты = ИскомыйВидОплаты;
НоваяСтрока.СуммаОплаты = СуммаИзФайла;
// ... дальнейшие действия, например, пересчет других данных
Сообщить("Добавлена новая оплата: " + ИскомыйВидОплаты);
КонецЕсли;
Этот подход является надежным и фундаментальным. Он явно показывает логику работы и легко модифицируется под более сложные условия проверки.
Платформа 1С предоставляет встроенный и более элегантный способ для решения этой задачи — метод табличной части НайтиСтроки(). Он позволяет избежать ручного перебора в цикле, делая код короче, читабельнее и, в большинстве случаев, производительнее.
Метод НайтиСтроки() принимает в качестве параметра Структуру, где ключами являются имена колонок, а значениями — искомые значения. В результате метод возвращает Массив строк, удовлетворяющих всем условиям из структуры.
Алгоритм действий:
Структуру для поиска. Вставляем в нее пары "ИмяКолонки" – "ИскомоеЗначение". Можно задавать условия сразу по нескольким колонкам.НайтиСтроки() для нашей табличной части, передав ему созданную структуру.Количество(). Если количество равно нулю, значит, ни одной подходящей строки не найдено.Посмотрим на пример:
// Данные для поиска
ИскомыйВидОплаты = Справочники.ВидыОплатОрганизаций.НайтиПоНаименованию("иОДИН");
СуммаИзФайла = 1500;
НаборЗаписей = ОбъектДок.Оплата;
// 1. Создание структуры для поиска
СтруктураПоиска = Новый Структура("ВидОплаты", ИскомыйВидОплаты);
// Если нужно искать по нескольким полям, можно добавить еще:
// СтруктураПоиска.Вставить("СуммаОплаты", 1500);
// 2. Поиск строк
НайденныеСтроки = НаборЗаписей.НайтиСтроки(СтруктураПоиска);
// 3. Проверка результата
Если НайденныеСтроки.Количество() = 0 Тогда
// 4. Строка не найдена, добавляем новую
НоваяСтрока = НаборЗаписей.Добавить();
НоваяСтрока.ВидОплаты = ИскомыйВидОплаты;
НоваяСтрока.СуммаОплаты = СуммаИзФайла;
Сообщить("Добавлена новая оплата через НайтиСтроки(): " + ИскомыйВидОплаты);
КонецЕсли;
Этот метод является предпочтительным для большинства подобных задач, так как он оптимизирован на уровне платформы и делает код более лаконичным и профессиональным.
Когда речь идет об обработке не одного-двух документов, а сотен или тысяч, оба предыдущих метода могут оказаться медленными. Это связано с тем, что для каждого документа нужно получить его объект (ПолучитьОбъект()), что является ресурсоемкой операцией. В таких случаях наиболее производительным решением будет использование запросов.
Идея заключается в том, чтобы не перебирать документы по одному, а одним запросом к базе данных сразу определить, в каких из них отсутствуют нужные нам записи.
Общая схема реализации:
ЛЕВОГО СОЕДИНЕНИЯ в запросе или программно.Этот подход сложнее в реализации, но он кардинально снижает нагрузку на систему и экономит время при работе с большими объемами данных, так как минимизирует количество обращений к объектной модели 1С.
В ходе обсуждения на форуме были выявлены типичные ошибки, которые важно не допускать.
1. Добавление записи внутри ветки Иначе в цикле.
Это самая распространенная логическая ошибка. Если искомая запись находится не на первом месте в табличной части, код добавит новую строку столько раз, сколько встретит несовпадений до нужной строки. Решение о добавлении новой записи всегда должно приниматься только после полного обхода или поиска по всей табличной части.
2. Использование Перейти ~Метка (GoTo).
В одном из вариантов решения автор использовал счетчик и оператор Перейти для выхода из цикла. Такой подход считается устаревшим и плохой практикой программирования. Он порождает так называемый "спагетти-код", который очень сложно читать, отлаживать и поддерживать. Вместо него всегда следует использовать структурированные операторы управления циклом: Прервать (для досрочного выхода из цикла) и Продолжить (для перехода к следующей итерации).