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