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