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