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