Как реализовать проверку на дубликаты в 1С и исключить текущий элемент из поиска

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

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

Разбор исходной проблемы

Рассмотрим ситуацию, с которой столкнулся автор темы. В справочнике УстройстваФотоВидеоФиксации есть реквизит КодПВК, который должен быть уникальным. Проверка реализована в стандартном обработчике ОбработкаПроверкиЗаполнения. Проанализируем исходный код, который вызвал затруднения:


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

Проблема этого кода заключается в том, что условие ГДЕ ищет любое совпадение по коду. Если мы открываем уже созданный ранее элемент и нажимаем "Записать", запрос находит этот же элемент в базе, видит, что КодПВК совпадает, и устанавливает Отказ = Истина. В итоге пользователь не может сохранить изменения, даже если он не менял сам код.

Решение: Исключение текущей ссылки

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

Нам потребуется передать в запрос ссылку на текущий объект. В модуле объекта ссылка доступна через свойство Ссылка. Обновим наш код, добавив параметр &ТекущаяСсылка.


Процедура ОбработкаПроверкиЗаполнения(Отказ, ПроверяемыеРеквизиты)
    
    // Если значение реквизита не заполнено, проверку на дубликаты обычно делать не нужно
    Если ПустаяСтрока(КодПВК) Тогда
        Возврат;
    КонецЕсли;

    Запрос = Новый Запрос;
    Запрос.Текст = 
    "ВЫБРАТЬ ПЕРВЫЕ 1
    |    УстройстваФотоВидеоФиксации.Ссылка КАК Ссылка
    |ИЗ
    |    Справочник.УстройстваФотоВидеоФиксации КАК УстройстваФотоВидеоФиксации
    |ГДЕ
    |    УстройстваФотоВидеоФиксации.КодПВК = &КодПВК
    |    И УстройстваФотоВидеоФиксации.Ссылка <> &ТекущаяСсылка
    |    И НЕ УстройстваФотоВидеоФиксации.ПометкаУдаления";
    
    Запрос.УстановитьПараметр("КодПВК", КодПВК);
    Запрос.УстановитьПараметр("ТекущаяСсылка", Ссылка);
    
    РезультатЗапроса = Запрос.Выполнить();
    
    Если НЕ РезультатЗапроса.Пустой() Тогда
        Отказ = Истина;
        
        Сообщение = Новый СообщениеПользователю;
        Сообщение.Текст = "Данный код ПВК уже используется в другом устройстве!";
        Сообщение.Поле = "КодПВК"; // Имя реквизита на форме
        Сообщение.УстановитьДанные(ЭтотОбъект);
        Сообщение.Сообщить();
    КонецЕсли;
КонецПроцедуры

Разберем подробнее внесенные изменения:

  1. Условие Ссылка <> &ТекущаяСсылка: Это ключевой момент. Если объект уже записан в базе, его ссылка будет исключена из результатов поиска. Если же объект новый, то Ссылка будет пустой (например, Справочники.УстройстваФотоВидеоФиксации.ПустаяСсылка()), и запрос корректно найдет дубликаты среди существующих записей.
  2. Использование ВЫБРАТЬ ПЕРВЫЕ 1: С точки зрения производительности нам не нужно знать все дубликаты. Нам достаточно факта существования хотя бы одного. Это ускоряет выполнение запроса на больших таблицах.
  3. Метод УстановитьДанные(ЭтотОбъект): Вместо ручного формирования пути к данным, использование этого метода в связке со свойством Поле позволяет платформе автоматически "подсветить" нужное поле на форме и активировать его при нажатии на сообщение об ошибке.

Важные нюансы проверки в «ОбработкеПроверкиЗаполнения»

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

1. Индексирование реквизита

Если в вашем справочнике тысячи или десятки тысяч записей, выполнение запроса по неиндексированному полю КодПВК приведет к полному сканированию таблицы (Table Scan). Это создаст серьезную нагрузку на СУБД. Проверьте в метаданных, чтобы у реквизита КодПВК свойство Индексировать было установлено в значение "Индексировать" или "Индексировать с доп. упорядочиванием".

2. Обработка пустых значений

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

3. Работа с пометкой на удаление

В исходном коде автора было условие НЕ ПометкаУдаления. Подумайте, допустимо ли использование кода, если старый элемент помечен на удаление? Обычно в учетных системах стараются избегать дублей даже среди удаленных объектов, чтобы не возникало путаницы в отчетах за прошлые периоды. Если уникальность должна быть абсолютной, условие по пометке удаления лучше убрать.

4. Программная запись и Обмен данными

Посмотрим на ситуацию, когда данные загружаются в базу программно (например, через Excel или при обмене с другой базой). В таких случаях стандартный обработчик ОбработкаПроверкиЗаполнения может не вызываться автоматически. Если проверка критически важна, следует предусмотреть дополнительный вызов метода ПроверитьЗаполнение() или вынести логику в ПередЗаписью. Также не забывайте проверять свойство ОбменДанными.Загрузка:


Если ОбменДанными.Загрузка Тогда
    Возврат;
КонецЕсли;

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

Альтернатива: Уникальные индексы на уровне платформы

Рассмотрим еще один способ решения задачи. Если реквизит КодПВК должен быть уникальным всегда и при любых условиях, самым надежным методом будет использование механизмов платформы. Для этого можно назначить данный реквизит Кодом справочника (если он подходит под формат) и установить свойство "Контроль уникальности".

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

← На главную