Как правильно получить объект из запроса 1С и оптимизировать обработку данных

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

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

Разбор синтаксической ошибки: почему Ссылка.ПолучитьОбъект() не работает

Проанализируем фрагмент кода, вызвавший затруднение у автора темы. Основная проблема заключалась в строке ЭлемДог = Ссылка.ПолучитьОбъект(); внутри цикла обхода выборки. Давайте выясним причину сбоя.

Когда мы работаем с результатом запроса, мы используем объект РезультатЗапроса и создаем на его основе ВыборкаИзРезультатаЗапроса. После вызова метода Выборка.Следующий(), платформа позиционируется на текущей строке результата, и все поля, которые были указаны в секции ВЫБРАТЬ, становятся доступными как свойства самой выборки.

В исходном коде автора переменная Ссылка не была определена в контексте программного модуля. Она существовала только внутри текста запроса. Чтобы получить значение ссылки в цикле, необходимо обращаться к ней через объект выборки. Рассмотрим правильный синтаксис:


Пока Выборка.Следующий() Цикл
    // Правильное обращение к полю выборки
    ТекущаяСсылка = Выборка.Ссылка; 
    ЭлемДог = ТекущаяСсылка.ПолучитьОбъект();
    
    Если ЭлемДог.ХозяйственнаяОперация = Перечисления.ХозяйственныеОперации.РеализацияКлиенту Тогда
        ПризРез = 1;
        Прервать;
    КонецЕсли;
КонецЦикла;

Важный момент: если в запросе вы используете псевдоним (например, ВЫБРАТЬ Ссылка КАК Договор), то в цикле нужно будет обращаться Выборка.Договор.

Проблема производительности: ловушка ПолучитьОбъект() в цикле

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

  1. Лишние обращения к БД: Запрос уже получил список ссылок. Однако метод ПолучитьОбъект() заставляет систему выполнить новый, отдельный запрос к базе данных для каждого элемента выборки. Если у контрагента 100 договоров, вы выполните 101 запрос вместо одного.
  2. Избыточность данных: Объект справочника содержит в себе абсолютно все данные: все реквизиты, все табличные части и метаданные. Если вам нужно проверить только одно поле (например, ХозяйственнаяОперация), получение всего объекта — это крайне нерациональный расход оперативной памяти и сетевого трафика.
  3. Блокировки: На уровне СУБД получение объекта может инициировать более тяжелые операции, чем простое чтение данных через запрос.

Проанализируем ситуацию: наша цель — всего лишь проверить значение реквизита. Для этого объект нам не нужен.

Оптимальное решение №1: Выбор полей в запросе

Вместо того чтобы "тянуть" весь объект из базы, мы просим запрос сразу вернуть нам значение нужного поля. Это самый правильный путь с точки зрения стандартов разработки 1С. Перепишем наш код:


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

Запрос.УстановитьПараметр("Контрагент", ТекКонтрагент);
РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();

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

В этом примере мы используем Выборка.ХозяйственнаяОперация. Данные были получены одним пакетом, что работает в десятки раз быстрее.

Оптимальное решение №2: Фильтрация на уровне СУБД

Рассмотрим ситуацию еще глубже. Нам не нужно перебирать все договоры в цикле Пока...КонецЦикла, если мы ищем наличие хотя бы одного подходящего под критерий. Мы можем переложить всю работу на сервер базы данных, добавив условие в секцию ГДЕ.

Проанализируем преимущества такого подхода:

Посмотрим на пример реализации:


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

Запрос.УстановитьПараметр("Контрагент", ТекКонтрагент);
Запрос.УстановитьПараметр("Операция", Перечисления.ХозяйственныеОперации.РеализацияКлиенту);

Результат = Запрос.Выполнить();
// Если результат не пустой, значит такой договор существует
ПризРез = ?(Результат.Пустой(), 0, 1);

В данном случае мы использовали конструкцию ВЫБРАТЬ ПЕРВЫЕ 1. Это дает команду СУБД прекратить поиск сразу после того, как будет найдена первая подходящая запись. Метод Результат.Пустой() позволяет нам мгновенно получить ответ без использования цикла.

Когда действительно нужно использовать ПолучитьОбъект()?

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

Если ваша задача — только чтение, проверка условий или вывод в отчет, всегда используйте запросы. Это золотое правило разработки на платформе 1С.

Рекомендации по именованию параметров

В ходе обсуждения на форуме было справедливо замечено, что имя параметра &Параметр является неудачным. Придерживайтесь следующих правил при написании запросов:

  1. Именуйте параметры согласно их смыслу (&Контрагент, &ДатаНачала).
  2. Используйте псевдонимы таблиц для улучшения читаемости кода (КАК Договоры).
  3. Всегда проверяйте типы передаваемых параметров. Передача "Битой ссылки" или значения неверного типа в &Параметр не вызовет ошибку выполнения запроса, но приведет к получению пустого результата, что бывает трудно отладить.

Подведем итог нашему разбору. Для исправления ошибки автора достаточно было обратиться к Выборка.Ссылка, но для создания качественного кода мы рекомендуем полностью отказаться от получения объекта в пользу условий внутри самого запроса. Это сделает ваше решение профессиональным, быстрым и устойчивым к росту объема данных в базе.

← На главную