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