Как исправить ошибку «Поле объекта не обнаружено» при выборке из табличной части в запросе 1С?

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

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

Причина ошибки: вложенные таблицы в языке запросов

Рассмотрим исходный запрос, вызвавший затруднение:


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

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

Проанализируем структуру полученного результата. Конструкция вида РеализацияТоваровУслуг.Товары.(...) создает вложенную таблицу. В результате выполнения такого запроса верхний уровень выборки содержит только строки документов, а сама табличная часть представлена отдельной колонкой Товары, содержащей вложенный объект типа РезультатЗапроса.

Поэтому обращение ВыборкаДетальныеЗаписи.Наименование завершается аварийно: у выборки верхнего уровня просто нет такого поля, оно находится глубже — внутри вложенной таблицы.

Способ 1. Запрос напрямую к табличной части документа (Рекомендуемый)

Если для решения задачи не требуется выводить шапку документа отдельно от строк, оптимальнее всего обратиться напрямую к табличной части документа через точку: Документ.РеализацияТоваровУслуг.Товары. В этом случае платформа возвращает плоскую таблицу, где каждая строка выборки соответствует отдельной строке табличной части.

Посмотрим на пример правильного формирования запроса:


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

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

Пока Выборка.Следующий() Цикл
    ОбластьСтрокаТЧ.Параметры.Добавка = Выборка.Наименование;
    ТабличныйДокумент.Вывести(ОбластьСтрокаТЧ);
КонецЦикла;

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

Способ 2. Обход вложенной выборки во встроенном языке

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

Разберем по шагам, как это реализовать:

  1. Выбираем записи верхнего уровня (документы).
  2. Внутри цикла получаем результат запроса вложенной таблицы через свойство ВыборкаДокументы.Товары.
  3. Создаем подчиненную выборку методом Выбрать() и обходим строки табличной части.

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

Пока ВыборкаДокументы.Следующий() Цикл
    // Получаем выборку по строкам текущего документа
    ВыборкаТовары = ВыборкаДокументы.Товары.Выбрать();
    
    Пока ВыборкаТовары.Следующий() Цикл
        // Здесь поле доступно напрямую
        ОбластьСтрокаТЧ.Параметры.Добавка = ВыборкаТовары.Наименование;
        ТабличныйДокумент.Вывести(ОбластьСтрокаТЧ);
    КонецЦикла;
КонецЦикла;

Сравнение значений ссылочных типов и перечислений

Следующая частая проблема возникает при попытке отфильтровать полученные данные по значению реквизита. Например, когда в цикле пишется следующее условие:


Если ВыборкаДетальныеЗаписи.Аналитика = "Добавка" Тогда 
    // Код не выполняется
КонецЕсли;

Выясним причину такого поведения: реквизит АналитикаБетона имеет тип ПеречислениеСсылка.ИмяПеречисления, а в условии выполняется сравнение со строковым литералом "Добавка". Так как типы данных не совпадают, условие всегда возвращает Ложь.

Использовать функцию Строка(Выборка.Аналитика) для сравнения крайне не рекомендуется. При изменении синонима перечисления код перестанет работать. Правильное сравнение во встроенном языке должно выполняться со ссылкой на само перечисление:


Если ВыборкаДетальныеЗаписи.Аналитика = Перечисления.АналитикаБетона.Добавка Тогда 
    ОбластьСтрокаТЧ.Параметры.Добавка = ВыборкаДетальныеЗаписи.Наименование;
    ТабличныйДокумент.Вывести(ОбластьСтрокаТЧ);
КонецЕсли;

Оптимизация: фильтрация данных в тексте запроса

С точки зрения производительности проверять условия в цикле обхода выборки неэффективно, так как сервер базы данных передает клиенту лишние строки. Лучше перенести отбор непосредственно в тело запроса с помощью конструкции ЗНАЧЕНИЕ.

Рассмотрим итоговый оптимизированный вариант запроса:


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

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

Пока Выборка.Следующий() Цикл
    ОбластьСтрокаТЧ.Параметры.Добавка = Выборка.Наименование;
    ТабличныйДокумент.Вывести(ОбластьСтрокаТЧ);
КонецЦикла;

Обратите внимание на формирование отчета: метод ТабличныйДокумент.Вывести() добавляет строку вертикально вниз. Если вызвать после него метод ТабличныйДокумент.Присоединить(ОбластьСтрокаТЧ) с той же областью, ячейки будут продублированы горизонтально вправо. Для стандартного табличного вывода используйте только метод Вывести().

← На главную