Как проверить, есть ли запись в табличной части, и добавить ее, если не найдена?

Программист 1С v8.3 (Обычные формы) 1C:Бухгалтерия Управленческий учет Торговля и дистрибуция
← На главную

При загрузке данных из внешних источников, например, из файла Excel, часто возникает задача: нужно добавить новые записи в табличную часть документа, но при этом избежать дублирования. Стандартный алгоритм выглядит так: для каждой строки из файла нужно проверить, существует ли уже такая запись в документе. Если да — переходим к следующей строке файла. Если же после проверки всей табличной части совпадений не найдено — добавляем новую строку. Разберем, как грамотно реализовать такую проверку в 1С.

Проанализируем ситуацию, с которой столкнулся автор. При обходе табличной части в цикле возникла сложность с тем, чтобы понять, что цикл завершился, а нужная запись так и не была найдена. Первоначальная попытка добавить строку в ветке Иначе внутри цикла — это логическая ошибка, которая приведет к многократному добавлению одной и той же записи. Рассмотрим несколько правильных подходов к решению этой задачи.

Метод 1: Классический подход с переменной-флагом

Это самый универсальный и понятный способ, который легко читается и подходит для любых, даже самых сложных, условий поиска. Логика проста: перед началом цикла мы "предполагаем", что записи нет, а если находим ее в процессе — "поднимаем флаг".

Рассмотрим по шагам:

  1. Перед циклом обхода табличной части создаем булеву переменную, например, ЗаписьНайдена, и присваиваем ей значение Ложь.
  2. Запускаем цикл Для Каждого ... Из ... для обхода строк табличной части.
  3. Внутри цикла сравниваем текущую строку с искомыми данными.
  4. Если совпадение найдено, присваиваем нашей переменной-флагу значение Истина и немедленно прерываем цикл с помощью оператора Прервать. Дальнейший поиск не имеет смысла, так как мы уже получили ответ на наш вопрос.
  5. После окончания цикла проверяем значение переменной-флага. Если она осталась равна Ложь, это означает, что цикл прошел полностью, и ни одна строка не подошла под условие. Только в этом случае мы добавляем новую запись.

Посмотрим на пример кода:


// Данные для поиска, полученные, например, из файла Excel
ИскомыйВидОплаты = Справочники.ВидыОплатОрганизаций.НайтиПоНаименованию("иОДИН");
СуммаИзФайла = 1500;

// Табличная часть документа
НаборЗаписей = ОбъектДок.Оплата;

// 1. Инициализация флага
Флаг_ЗаписьНайдена = Ложь; 

// 2. Обход в цикле
Для Каждого Запись Из НаборЗаписей Цикл
    Если Запись.ВидОплаты = ИскомыйВидОплаты Тогда
        // 3. Если запись найдена, меняем флаг и выходим
        Флаг_ЗаписьНайдена = Истина;
        Прервать; // Выход из цикла, так как запись уже существует
    КонецЕсли;
КонецЦикла;

// 4. Проверка флага и добавление записи
Если НЕ Флаг_ЗаписьНайдена Тогда
    // Запись не была найдена, значит, можно добавить новую
    НоваяСтрока = НаборЗаписей.Добавить();
    НоваяСтрока.ВидОплаты = ИскомыйВидОплаты;
    НоваяСтрока.СуммаОплаты = СуммаИзФайла;
    
    // ... дальнейшие действия, например, пересчет других данных
    Сообщить("Добавлена новая оплата: " + ИскомыйВидОплаты);
КонецЕсли;

Этот подход является надежным и фундаментальным. Он явно показывает логику работы и легко модифицируется под более сложные условия проверки.

Метод 2: Рекомендуемый метод с `НайтиСтроки()`

Платформа 1С предоставляет встроенный и более элегантный способ для решения этой задачи — метод табличной части НайтиСтроки(). Он позволяет избежать ручного перебора в цикле, делая код короче, читабельнее и, в большинстве случаев, производительнее.

Метод НайтиСтроки() принимает в качестве параметра Структуру, где ключами являются имена колонок, а значениями — искомые значения. В результате метод возвращает Массив строк, удовлетворяющих всем условиям из структуры.

Алгоритм действий:

  1. Создаем новую Структуру для поиска. Вставляем в нее пары "ИмяКолонки" – "ИскомоеЗначение". Можно задавать условия сразу по нескольким колонкам.
  2. Вызываем метод НайтиСтроки() для нашей табличной части, передав ему созданную структуру.
  3. Проверяем количество элементов в полученном массиве с помощью метода Количество(). Если количество равно нулю, значит, ни одной подходящей строки не найдено.
  4. Если строки не найдены, добавляем новую запись.

Посмотрим на пример:


// Данные для поиска
ИскомыйВидОплаты = Справочники.ВидыОплатОрганизаций.НайтиПоНаименованию("иОДИН");
СуммаИзФайла = 1500;

НаборЗаписей = ОбъектДок.Оплата;

// 1. Создание структуры для поиска
СтруктураПоиска = Новый Структура("ВидОплаты", ИскомыйВидОплаты);
// Если нужно искать по нескольким полям, можно добавить еще:
// СтруктураПоиска.Вставить("СуммаОплаты", 1500);

// 2. Поиск строк
НайденныеСтроки = НаборЗаписей.НайтиСтроки(СтруктураПоиска);

// 3. Проверка результата
Если НайденныеСтроки.Количество() = 0 Тогда
    // 4. Строка не найдена, добавляем новую
    НоваяСтрока = НаборЗаписей.Добавить();
    НоваяСтрока.ВидОплаты = ИскомыйВидОплаты;
    НоваяСтрока.СуммаОплаты = СуммаИзФайла;
    
    Сообщить("Добавлена новая оплата через НайтиСтроки(): " + ИскомыйВидОплаты);
КонецЕсли;

Этот метод является предпочтительным для большинства подобных задач, так как он оптимизирован на уровне платформы и делает код более лаконичным и профессиональным.

Метод 3: Оптимизация для массовых операций с помощью запроса

Когда речь идет об обработке не одного-двух документов, а сотен или тысяч, оба предыдущих метода могут оказаться медленными. Это связано с тем, что для каждого документа нужно получить его объект (ПолучитьОбъект()), что является ресурсоемкой операцией. В таких случаях наиболее производительным решением будет использование запросов.

Идея заключается в том, чтобы не перебирать документы по одному, а одним запросом к базе данных сразу определить, в каких из них отсутствуют нужные нам записи.

Общая схема реализации:

  1. Собираем список всех документов, которые потенциально могут потребовать изменения (например, все "Отчеты о розничных продажах" за определенный период).
  2. Пишем запрос, который из табличной части "Оплата" этих документов выбирает ссылки на документы, в которых уже есть нужные нам виды оплат.
  3. Сравниваем два списка. Нам нужны те документы, которые есть в первом списке (все документы периода), но отсутствуют во втором (те, где уже есть нужные записи). Это можно сделать с помощью ЛЕВОГО СОЕДИНЕНИЯ в запросе или программно.
  4. Проходим циклом только по отфильтрованному, гораздо меньшему списку "проблемных" документов, получаем их объекты и гарантированно добавляем недостающие строки.

Этот подход сложнее в реализации, но он кардинально снижает нагрузку на систему и экономит время при работе с большими объемами данных, так как минимизирует количество обращений к объектной модели 1С.

Чего следует избегать: Распространенные ошибки

В ходе обсуждения на форуме были выявлены типичные ошибки, которые важно не допускать.

1. Добавление записи внутри ветки Иначе в цикле.

Это самая распространенная логическая ошибка. Если искомая запись находится не на первом месте в табличной части, код добавит новую строку столько раз, сколько встретит несовпадений до нужной строки. Решение о добавлении новой записи всегда должно приниматься только после полного обхода или поиска по всей табличной части.

2. Использование Перейти ~Метка (GoTo).

В одном из вариантов решения автор использовал счетчик и оператор Перейти для выхода из цикла. Такой подход считается устаревшим и плохой практикой программирования. Он порождает так называемый "спагетти-код", который очень сложно читать, отлаживать и поддерживать. Вместо него всегда следует использовать структурированные операторы управления циклом: Прервать (для досрочного выхода из цикла) и Продолжить (для перехода к следующей итерации).

← На главную