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

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

При разработке и доработке печатных форм, отчетов или алгоритмов проведения документов разработчики нередко сталкиваются с ошибкой выполнения запроса вида: {(Строка, Колонка)}: Неоднозначное поле "ИмяТаблицы.Ссылка". Разберем подробно, почему платформа 1С выдает такое сообщение и как быстро и правильно устранить эту проблему.

Проанализируем типовую ситуацию на практическом примере. При попытке сформировать печатную форму документа СписаниеТоваров возникает исключение:


{Документ.СписаниеТоваров.МодульОбъекта(928)}: Ошибка при вызове метода контекста (Выполнить)
    Док = Запрос.Выполнить().Выбрать();
по причине:
{(17, 2)}: Неоднозначное поле "СписаниеТоваров.Ссылка"
<<?>>СписаниеТоваров.Ссылка = &ТекДокумент

Посмотрим на исходный текст запроса, вызвавший данное исключение:


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

Док = Запрос.Выполнить().Выбрать();

Выясним причины возникновения ошибки

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

  1. Совпадение псевдонима таблицы с именем объекта метаданных или реквизита: если алиас таблицы задан как СписаниеТоваров, и при этом в конфигурации существует одноименный реквизит, табличная часть или соединяемая таблица с похожей структурой, платформа не может определить, обращается разработчик к таблице-источнику или к реквизиту.
  2. Использование вложенных выборок табличных частей: конструкция вида СписаниеТоваров.ИнвентаризационнаяКомиссия.(...) неявно создает вложенную выборку. Строки табличной части также содержат скрытое системное поле Ссылка (ссылку на документ-владелец). При совпадении имени псевдонима основной таблицы с именем метаданных парсер путает уровень шапки документа и уровень строк вложенной таблицы.
  3. Влияние ограничения доступа на уровне записей (RLS): при указании ключевого слова ВЫБРАТЬ РАЗРЕШЕННЫЕ платформа динамически дописывает шаблоны ограничений прав доступа в секцию ГДЕ. Если псевдоним таблицы совпадает с именем метаданных, внедряемый код условий RLS конфликтует с текстом запроса, что приводит к неоднозначности. Именно поэтому в базе с полными правами запрос может работать корректно, а у пользователя с ограниченными правами падать с ошибкой.

Способ 1. Назначение уникального псевдонима таблицы (быстрое решение)

Рассмотрим самое простое и быстрое исправление — переименование псевдонима (алиаса) таблицы в секции ИЗ на уникальное имя, которое не дублирует имя метаданных конфигурации.


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

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

Заменив псевдоним СписаниеТоваров на ДокСписание (или ШапкаДокумента), мы полностью исключаем конфликт имен для транслятора запросов 1С.

Способ 2. Перевод запроса на пакетную выборку (стандарт разработки)

Разберем более надежный и профессиональный подход. Выборка табличных частей через точку в основном запросе (вложенные таблицы) считается устаревшей практикой: она создает избыточную нагрузку на СУБД и часто провоцирует ошибки неоднозначности полей при наложении условий RLS. Стандарты разработки 1С рекомендуют использовать пакетные запросы с методом ВыполнитьПакет().


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

МассивРезультатов = Запрос.ВыполнитьПакет();
ВыборкаШапка = МассивРезультатов[0].Выбрать();
ВыборкаКомиссия = МассивРезультатов[1].Выбрать();

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

← На главную