Как объединить данные документа и среза последних регистра цен в одном запросе без временных таблиц

Программист Управленческий учет
← На главную

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

Метод 1. Использование вложенного запроса в секции соединения

Самый прямой способ объединить две таблицы без использования временных таблиц — это поместить один из запросов внутрь другого в качестве источника данных. В языке запросов 1С это называется «Вложенный запрос». Проанализируем, как это реализовать на примере объединения запасов документа и цен номенклатуры.

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

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


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

Разберем, что мы сделали. Мы создали виртуальную таблицу ВложенныйЗапросЦены непосредственно в секции ИЗ (через соединение). Теперь поле ЦенаЗП доступно для выбора в основном списке ВЫБРАТЬ. Обратите внимание на использование функции ЕСТЬNULL: если для какой-то номенклатуры цена не будет найдена, запрос вернет 0 вместо пустого значения, что важно для дальнейших расчетов.

Оптимизация виртуальных таблиц

Проанализируем важный аспект производительности. При использовании виртуальной таблицы СрезПоследних крайне важно передавать условия (например, фильтр по виду цен) в параметры самой таблицы, а не в общую секцию ГДЕ. Если написать условие ГДЕ ВидЦен.Код = "000000015" в конце общего запроса, система сначала получит срез последних цен по всем видам цен для всех товаров, и только потом отфильтрует их. Это может привести к значительным задержкам. Мы же указали фильтр внутри параметров виртуальной таблицы, что заставляет СУБД работать только с нужным подмножеством данных.

Метод 2. Связи наборов данных в СКД (рекомендуемый для отчетов)

Если ваша задача — вывести данные в отчете на базе СКД, то использование одного сложного запроса с вложенными подзапросами часто является избыточным и менее производительным. Выясним, почему «не получилось вывести поля из временной таблицы» у автора темы. Обычно это происходит из-за того, что СКД не всегда корректно анализирует структуру сложных пакетов запросов, если не настроены псевдонимы или роли полей.

Вместо мучений с объединением в одном тексте запроса, мы можем использовать механизм Связи наборов данных:

  1. Создадим НаборДанных1 (запрос), который выбирает только данные из документа НарядЗаказ.
  2. Создадим НаборДанных2 (запрос), который выбирает цены из регистра ЦеныНоменклатуры.СрезПоследних. В параметрах виртуальной таблицы укажем условие по виду цен.
  3. Перейдем на закладку Связи наборов данных.
  4. Укажем связь: Источник — НаборДанных1, Приемник — НаборДанных2. Условие связи — Номенклатура = Номенклатура.
  5. В параметрах связи для НабораДанных2 укажем передачу параметра Период и, если нужно, передадим конкретную номенклатуру в условие второго запроса.

Такой подход позволяет СКД самостоятельно оптимизировать запросы. Например, для каждой строки из первого набора данных система может эффективно получить цену из второго, не выгружая весь справочник цен в память.

Разбор типичных синтаксических ошибок

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

Также важно помнить про оператор В. Если подзапрос может вернуть более одного значения (хотя для среза последних по конкретной номенклатуре и виду цен это редкость), использование оператора = приведет к ошибке выполнения. В таких случаях мы используем В (ВЫБРАТЬ...).

Подведем итог: для решения задачи без временных таблиц мы выбираем либо Левое соединение с вложенным запросом (если нужен один текст запроса), либо Связи наборов данных в СКД (если мы разрабатываем отчет). Оба метода позволяют гибко управлять данными и получать необходимые показатели в одной результирующей таблице.

← На главную