Как в запросе 1С добавить данные к строкам табличной части документа через левое соединение?

Программист 1С v8.3 (Обычные формы) Управленческий учет Торговля и дистрибуция
← На главную

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

Проблема использования вложенных таблиц в соединениях

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

Проанализируем фрагмент ошибочного кода:


|ВЫБРАТЬ
|   Инвентаризация.Номер,
|   Инвентаризация.Товары.(
|       Номенклатура,
|       Количество
|   )
|ИЗ
|   Документ.ИнвентаризацияТоваровНаСкладе КАК Инвентаризация
|ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.Цены.СрезПоследних КАК Цены
|ПО Инвентаризация.Товары.Номенклатура = Цены.Номенклатура // ОШИБКА!

В данном случае поле Инвентаризация.Товары.Номенклатура не является доступным для условия связи, так как оно находится внутри коллекции. Платформа просто "не видит" строки табличной части в контексте основного соединения документа.

Решение №1: Прямой запрос к табличной части

Самый эффективный и правильный способ решить эту задачу — сменить объект метаданных, к которому мы обращаемся в секции ИЗ. Вместо того чтобы выбирать документ и "раскрывать" его таблицу, мы выберем саму таблицу как самостоятельный плоский список строк. Выясним, как это меняет структуру данных: каждая строка результата теперь будет соответствовать одной строке документа, а реквизиты шапки (номер, дата, склад) мы сможем получить "через точку" от реквизита Ссылка.

Рассмотрим правильный пример построения запроса:


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

В этом примере левое соединение работает корректно, так как ИнвТовары.Номенклатура — это обычное поле в плоской таблице. Использование функции ЕСТЬNULL позволяет нам избежать проблем при отсутствии цены в регистре, подставляя ноль вместо пустого значения.

Решение №2: Использование итогов для иерархической выборки

Если нам необходимо программно обрабатывать результат так, чтобы сначала шла шапка документа, а потом цикл по строкам (как это было в исходном вопросе автора через ВыборкаСтрокТовары = Шапка.Товары.Выбрать()), нам следует использовать конструкцию ИТОГИ. Это позволит платформе сгруппировать плоские строки обратно в удобную структуру.

Разберем по шагам, как модифицировать запрос и программный код:

  1. В тексте запроса в самом конце добавим ИТОГИ ПО Ссылка (или по всем реквизитам шапки).
  2. При обходе результата используем метод Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам).

Пример реализации:


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

Результат = Запрос.Выполнить();
ВыборкаШапка = Результат.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);

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

Оптимизация: передача фильтра в виртуальную таблицу

Посмотрим на ситуацию со стороны производительности. Если документ содержит сотни строк, а регистр цен — миллионы, простое левое соединение может работать медленно. Проанализируем запрос из сообщения №11 форума. Автор пытался использовать подзапрос в параметрах виртуальной таблицы. Это правильное направление мысли, но синтаксически это лучше оформить через временные таблицы.

Рекомендация: Сначала выгрузите уникальные товары документа во временную таблицу, проиндексируйте их, а затем соединяйте с регистром. Это заставит сервер базы данных работать только с нужным подмножеством цен.

Пример оптимизированного подхода:

  1. Создаем первый запрос: выбираем Номенклатура из ТЧ документа во временную таблицу ВТ_Товары.
  2. Устанавливаем индекс на поле Номенклатура в этой временной таблице.
  3. Во втором запросе соединяем ВТ_Товары с РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&Дата, Номенклатура В (ВЫБРАТЬ Номенклатура ИЗ ВТ_Товары)).

Важные нюансы при работе с ценами

При выполнении соединения важно учитывать следующие моменты:

Таким образом, мы выяснили, что для добавления данных к строкам табличной части необходимо "развернуть" запрос, сделав саму табличную часть основным источником данных. Это открывает возможности для полноценного использования механизмов ЛЕВОЕ СОЕДИНЕНИЕ, индексации и оптимизации запросов, обеспечивая высокую скорость работы и точность получаемых данных.

← На главную