Как проверить наличие данных в регистре сведений и избежать дублей при записи

Программист 1С v8.3 (Управляемые формы) Управленческий учет
← На главную

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

Метод 1. Использование запроса с левым соединением

Это наиболее эффективный и правильный с точки зрения производительности способ, особенно если строк в табличной части документа много. Вместо того чтобы проверять каждую строку в цикле (что порождает избыточные обращения к базе данных), мы одним запросом определим, каких данных еще нет в регистре.

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

Рассмотрим пример реализации такого запроса:


// Создаем запрос для получения только новых строк
Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ РАЗЛИЧНЫЕ
    |	ДокТЧ.Упражнение КАК Упражнение,
    |	ДокТЧ.Вес КАК Вес
    |ПОМЕСТИТЬ ВТ_ДанныеДокумента
    |ИЗ
    |	&ТаблицаДанных КАК ДокТЧ
    |;
    |
    |////////////////////////////////////////////////////////////////////////////////
    |ВЫБРАТЬ
    |	ВТ.Упражнение,
    |	ВТ.Вес
    |ИЗ
    |	ВТ_ДанныеДокумента КАК ВТ
    |		ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ИсторияТренировок КАК Рег
    |		ПО ВТ.Упражнение = Рег.Упражнение
    |			И ВТ.Вес = Рег.Вес
    |ГДЕ
    |	Рег.Упражнение ЕСТЬ NULL";

// Передаем таблицу части документа в запрос
Запрос.УстановитьПараметр("ТаблицаДанных", Объект.ВыполненныеУпражнения.Выгрузить());

РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();

Пока Выборка.Следующий() Цикл
    // Здесь выполняем запись в регистр
КонецЦикла;

Важный момент: использование конструкции ВЫБРАТЬ РАЗЛИЧНЫЕ внутри запроса или предварительное использование метода Свернуть() для таблицы значений документа позволяет избежать дублей внутри самой транзакции записи, если пользователь случайно ввел одинаковые строки в одном документе.

Метод 2. Проверка через Менеджер Записи

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

Разберем алгоритм работы с менеджером записи по шагам:

  1. Создаем новый менеджер записи для нужного регистра.
  2. Заполняем все измерения регистра значениями из нашей строки.
  3. Вызываем метод Прочитать().
  4. Проверяем свойство Выбран(). Если оно возвращает Ложь, значит, такой комбинации измерений в базе нет.

Пример кода для этого метода:


Для Каждого СтрокаТЧ Из Объект.ВыполненныеУпражнения Цикл
    МенеджерЗаписи = РегистрыСведений.ИсторияТренировок.СоздатьМенеджерЗаписи();
    
    // Заполняем измерения для поиска
    МенеджерЗаписи.Упражнение = СтрокаТЧ.Упражнение;
    МенеджерЗаписи.Вес = СтрокаТЧ.Вес;
    
    // Пытаемся прочитать запись из базы
    МенеджерЗаписи.Прочитать();
    
    Если Не МенеджерЗаписи.Выбран() Тогда
        // Записи нет, можем заполнять ресурсы и записывать
        МенеджерЗаписи.ДатаПервогоПодхода = ТекущаяДата();
        МенеджерЗаписи.Записать();
    КонецЕсли;
КонецЦикла;

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

Метод 3. Программное сравнение таблиц значений

В некоторых случаях, например, при работе на мобильной платформе с ограниченными ресурсами или специфической логикой синхронизации, бывает удобно выгрузить данные регистра в таблицу значений и сравнить их в оперативной памяти. Рассмотрим, как ускорить этот процесс с помощью индексации.

Если мы просто будем искать строку в таблице перебором, это займет много времени. Поэтому мы добавим индекс по ключевым полям:


// Выгружаем существующие записи регистра в ТЗ
ТаблицаРегистра = РегистрыСведений.ИсторияТренировок.СрезПоследних(); // Или просто Выбрать()
ТаблицаРегистра.Индексы.Добавить("Упражнение, Вес");

ТаблицаДокумента = Объект.ВыполненныеУпражнения.Выгрузить();

Для Каждого СтрокаДок Из ТаблицаДокумента Цикл
    ПараметрыОтбора = Новый Структура("Упражнение, Вес", СтрокаДок.Упражнение, СтрокаДок.Вес);
    НайденныеСтроки = ТаблицаРегистра.НайтиСтроки(ПараметрыОтбора);
    
    Если НайденныеСтроки.Количество() = 0 Тогда
        // Таких данных в регистре нет, добавляем в список на запись
    КонецЕсли;
КонецЦикла;

Архитектурный совет: Переход к периодическим регистрам

Проанализируем ситуацию, описанную в обсуждении: автору важно знать, когда он впервые выполнил упражнение с определенным весом. Использование непериодического регистра с проверкой на "уникальность" может быть избыточным усложнением.

Рекомендуемое решение: сделать регистр сведений периодическим (с периодичностью "В пределах секунды") и подчиненным регистратору. В этом случае:

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

Особенности мобильной платформы 1С

Выясним причину, почему на мобильных устройствах стоит быть внимательнее к проверкам:

  1. SQLite: Мобильная платформа использует движок SQLite. Запросы в цикле там работают медленнее, чем на полноценном SQL-сервере. Старайтесь всегда использовать пакетные запросы или временные таблицы.
  2. Синхронизация: Если ваше приложение обменивается данными с центральной базой, помните, что локальная проверка на устройстве не гарантирует уникальности в основной базе. Если два пользователя одновременно введут одну и ту же "уникальную" запись, конфликт возникнет на этапе синхронизации.
  3. Индексация: Обязательно проверяйте, что поля Упражнение и Вес являются измерениями регистра. В 1С измерения индексируются автоматически, что критически важно для быстродействия запросов типа ЛЕВОЕ СОЕДИНЕНИЕ.

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

← На главную