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