1с получить объект по ссылке в запросе

Узнайте, как быстро получить объект по ссылке в запросе 1С. Простой способ без лишнего кода и ошибок для новичков и профи.

Бесплатная консультация юриста

+7 (800) 302-87-29

Реклама jurik.ru

В разработке на платформе 1С:Предприятие 8 одной из самых частых задач является работа с данными, полученными в результате выполнения запроса. Часто разработчики сталкиваются с необходимостью не просто прочитать значения полей из выборки, а получить полноценный объект метаданных (например, ДокументОбъект, СправочникОбъект, БизнесПроцессОбъект) для его последующего изменения, записи или вызова методов.

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

Теоретические основы: Ссылка vs Объект

Прежде чем переходить к коду, важно четко разграничивать два понятия:

  • Ссылка (Ref) — это легковесный идентификатор объекта в базе данных (аналог первичного ключа). Она содержит только уникальный идентификатор (UUID) и указатель на метаданные (вид объекта). Ссылки возвращаются запросом по полям типа «Ссылка». Они не содержат данных объекта (реквизитов, табличных частей). Чтение ссылок очень быстрое.
  • Объект (Object), это полноценный управляемый объект платформы (ДокументОбъект.Имя, СправочникОбъект.Имя и т;д.). Он содержит все реквизиты, табличные части, наборы записей и предоставляет методы Записать, Удалить, Провести и др. Получение объекта — это дорогая операция (обращение к БД за полным набором данных).

Ключевой момент: Запрос (Запрос) никогда не возвращает объекты. Он возвращает только ссылки (или примитивные типы: строки, числа, даты, булево). Преобразование ссылки в объект происходит после выполнения запроса, в коде 1С.

Основной метод: ПолучитьОбъект

У любого значения типа Ссылка (например, СправочникСсылка.Номенклатура, ДокументСсылка.РеализацияТоваровУслуг) есть метод ПолучитьОбъект.

Синтаксис:

Объект = Ссылка.ПолучитьОбъект;

Пример базового использования в цикле выборки:

Запрос = Новый Запрос;
Запрос.Текст = 
 "ВЫБРАТЬ
 | Ссылка
 |ИЗ
 | Документ.РеализацияТоваровУслуг
 |ГДЕ
 | Проведен = ИСТИНА
 | И Дата МЕЖДУ &НачПериода И &КонПериода";

Запрос.УстановитьПараметр("НачПериода", НачалоДня(ТекущаяДата));
Запрос.УстановитьПараметр("КонПериода", КонецДня(ТекущаяДата));

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

Пока Выборка.Следующий Цикл
 // Выборка.Ссылка имеет тип ДокументСсылка.РеализацияТоваровУслуг
 ДокументОбъект = Выборка.Ссылка.ПолучитьОбъект; 
 
 // Теперь можем работать с полным объектом
 ДокументОбъект.Комментарий = "Обработано скриптом " + ТекущаяДата;
 
 // Важно: запись объекта — отдельная операция
 ДокументОбъект.Записать(РежимЗаписиДокумента.Проведение); 
КонецЦикла;

Сценарии использования и лучшие практики

Чтение данных: Не используйте ПолучитьОбъект без необходимости

Это самая частая ошибка новичков. Если вам нужно только прочитать несколько реквизитов (Наименование, Сумма, Контрагент), не вызывайте ПолучитьОбъект. Укажите нужные поля прямо в тексте запроса.

Плохо (медленно, нагружает БД и память):

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

Хорошо (быстро, оптимально):

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

Массовое получение объектов: Метод Выгрузить

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

Результат = Запрос.Выполнить;
// Выгружаем колонку "Ссылка" в массив
МассивСсылок = Результат.ВыгрузитьКолонку("Ссылка"); 

МассивОбъектов = Новый Массив;
Для Каждого Ссылка Из МассивСсылок Цикл
 МассивОбъектов.Добавить(Ссылка.ПолучитьОбъект);
КонецЦикла;

// Пример пакетной записи (если объекты изменялись)
// Документы.РеализацияТоваровУслуг.ЗаписатьПакетом(МассивОбъектов);

Работа с табличными частями

При получении объекта через ПолучитьОбъект загружаются все табличные части документа/справочника. Если объект большой (много строк ТЧ), это существенно увеличивает потребление памяти и время выполнения.

Если нужно изменить только шапку документа — это нормально. Если нужно проанализировать строки ТЧ — иногда эффективнее выбрать данные ТЧ отдельным запросом (через Документ.Товары), а объект получать только для записи изменений.

Особенности в подписках на события (Event Subscriptions)

Контекст, упомянутый в ваших исходных данных (подписка на событие ОбработкаПолученияФормы), требует особого внимания. В подписках на события (особенно ПередЗаписью, ПриЗаписи) параметр Источник часто уже является Объектом, а не ссылкой.

  • Источник в подписке на событие менеджера (ДокументМенеджер) — это Менеджер.
  • Источник в подписке на событие объекта (ДокументОбъект) — это сам Объект.
  • В параметрах формы (Параметры.Ключ) передается именно Ссылка.

Правильный паттерн для подписки на ОбработкаПолученияФормы (Управляемые формы):

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

КонецПроцедуры

Важно: В обработчиках форм (ОбработкаПолученияФормы) старайтесь не вызывать ПолучитьОбъект без крайней необходимости. Форма сама загрузит данные. Используйте ВыбраннаяФорма для управления UI (доступность, видимость, условное оформление).

Типичные ошибки и подводные камни

Ошибка 1: Попытка вызвать ПолучитьОбъект внутри текста запроса

Неверно:

Запрос.Текст = "ВЫБРАТЬ Ссылка.ПолучитьОбъект КАК Объект ИЗ Документ.Продажа"; // ОШИБКА КОМПИЛЯЦИИ ЗАПРОСА

Язык запросов 1С не знает методов платформы ПолучитьОбъект. Запрос работает на уровне СУБД.

Ошибка 2: Получение объекта для ссылки, которой нет в БД (Удаленная/Пустая)

Ссылка = Документы.РеализацияТоваровУслуг.ПустаяСсылка;
Объект = Ссылка.ПолучитьОбъект; // Вернет НОВЫЙ пустой объект (аналог СоздатьДокумент), не ошибку!

// Но если ссылка указывала на удаленный объект:
Объект = УдаленнаяСсылка.ПолучитьОбъект; // Исключение: "Объект не найден"

Всегда проверяйте Ссылка.Пустая или используйте Попытка ... Исключение ... КонецПопытки при работе с внешними данными.

Ошибка 3: Модификация объекта «Только чтение»

Объекты, полученные через ВыборкаСсылки.ПолучитьОбъект в отчетах или обработках (где нет блокировки), могут быть в режиме «Только чтение» при попытке записи, если не установлены соответствующие права или режим блокировки.

Для гарантированной записи используйте:

Объект = Ссылка.ПолучитьОбъект(РежимБлокировкиОбъекта.Запись); // Явный запрос блокировки на запись
Объект.Реквизит = Значение;
Объект.Записать;

Ошибка 4: Бесконечные циклы при записи в подписке «ПередЗаписью»

// ОПАСНО!
Процедура ПередЗаписью(Источник, Отказ, РежимЗаписи, РежимПроведения)
 // Источник ― это уже ОБЪЕКТ (ДокументОбъект)
 // Если здесь вызвать Источник.ПолучитьОбъект ― получим копию или ошибку рекурсии
 // Если здесь вызвать Источник.Записать — будет рекурсивный вызов ПередЗаписью -> StackOverflow
 
 // Правильно: меняем реквизиты Источника напрямую
 Источник.Комментарий = "Изменено в подписке";
КонецПроцедуры

Производительность: Запрос vs ПолучитьОбъект

Подход Скорость Память Когда применять
Поля в SELECT (Запрос.Выполнить.Выбрать) Очень высокая Минимальная Отчеты, списки, чтение данных, фильтрация, расчеты.
Выборка.Ссылка.ПолучитьОбъект в цикле Низкая (N запросов к БД) Высокая (все реквизиты + ТЧ) Изменение объектов, запись, проведение, сложная логика, требующая методов объекта.
Результат.ВыгрузитьКолонку(«Ссылка») + цикл ПолучитьОбъект Низкая Высокая Подготовка массива для ЗаписатьПакетом / ПровестиПакетом.

Золотое правило: Сначала отберите максимум данных запросом (поля, агрегаты, соединения). Получайте объекты (ПолучитьОбъект) только для тех записей, которые реально требуют изменения или вызова методов объекта.

Продвинутые техники

Временные таблицы и пакетная обработка

Для обработки тысяч документов неэффективно вызывать ПолучитьОбъект в цикле по одному. Используйте временные таблицы менеджера записи:

// 1. Запрос отбирает ссылки на документы для обработки
Запрос.Текст = "ВЫБРАТЬ Ссылка ИЗ Документ.ПеремещениеТоваров ГДЕ Проведен = ЛОЖЬ";
МассивСсылок = Запрос.Выполнить.ВыгрузитьКолонку("Ссылка");

// 2. Менеджер записи позволяет работать с набором объектов эффективно
НаборЗаписей = Документы.ПеремещениеТоваров.СоздатьНаборЗаписей;
НаборЗаписей.Отбор.Ссылка.Установить(МассивСсылок); // Загружаем объекты пачкой (оптимизированно)
НаборЗаписей.Прочитать; // Один запрос к БД на чтение всех объектов

Для Каждого ДокументОбъект Из НаборЗаписей Цикл
 ДокументОбъект.Проведен = Истина;
 // Логика заполнения...
КонецЦикла;

НаборЗаписей.Записать; // Пакетная запись в одну транзакцию

ПолучитьОбъект с параметром РежимБлокировкиОбъекта

Позволяет избежать ошибок «Объект изменен другим пользователем» (конфликт блокировок) при параллельной работе.

Попытка
 // Пытаемся получить объект с блокировкой на запись (помещаем в очередь ожидания)
 ДокументОбъект = Ссылка.ПолучитьОбъект(РежимБлокировкиОбъекта.Запись, Истина); // Истина = Ждать разблокировки
 
 ДокументОбъект.Склад = НовыйСклад;
 ДокументОбъект.Записать;
 
Исключение
 Сообщить("Не удалось заблокировать документ для редактирования: " + ОписаниеОшибки);
КонецПопытки;

Резюме (Checklist)

  1. Запрос возвращает Ссылки. Объектов в результате запроса не существует.
  2. Используйте Ссылка.ПолучитьОбъект только в коде 1С (модуле) после Выборка.Следующий.
  3. Для чтения данных — выбирайте поля в запросе. Не читайте объект ради одного реквизита.
  4. Для массовых изменений, используйте НаборЗаписей или ЗаписатьПакетом. Это в разы быстрее цикла с ПолучитьОбъект.
  5. В подписках на события проверяйте тип Источник: часто это уже Объект, и повторное получение не нужно.
  6. В формах управляйте UI через ВыбраннаяФорма.Элементы, а не через чтение объекта БД в ОбработкаПолученияФормы.
  7. Обрабатывайте исключения при получении объекта (объект мог быть удален другим пользователем).

Грамотное разделение этапов «Чтение данных (Запрос)» и «Работа с бизнес-логикой (Объект)» — залог быстрых, надежных и масштабируемых решений на платформе 1С.