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