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