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