Как получить по 10 номенклатур из каждой подгруппы в 1С одним запросом?

Программист 1С v8.3 (Управляемые формы) 1С:Управление торговлей Управленческий учет Торговля и дистрибуция
← На главную

При разработке обработок автоматического заполнения документов (например, «Приобретение товаров и услуг» в УТ 11) часто возникает задача: выбрать не всю номенклатуру из группы, а лишь ограниченное количество из каждой вложенной подгруппы. Основная сложность здесь заключается в иерархической структуре справочника Номенклатура и требовании выполнить задачу одним запросом, без использования запросов в цикле, что является стандартом качественной разработки на платформе 1С.

В этой статье мы разберем, как реализовать такую логику, проанализируем подходы с использованием временных таблиц и функции АВТОНОМЕРЗАПИСИ, а также рассмотрим более современные методы, доступные в новых версиях платформы.

Анализ задачи и подготовка данных

Разберем ситуацию: у нас есть корневая группа (папка), в которой может быть множество подгрупп любого уровня вложенности. Нам необходимо зайти в каждую «конечную» папку и взять оттуда до 10 элементов (если элементов меньше 10 — берем все, что есть). Если делать это в цикле по папкам, производительность системы значительно упадет, особенно при большом количестве номенклатуры. Поэтому мы будем использовать временные таблицы.

Сначала нам необходимо определить список всех групп, которые входят в выбранную иерархию. Рассмотрим первый шаг — получение всех родительских групп:


ВЫБРАТЬ
    Номенклатура.Ссылка КАК Ссылка
ПОМЕСТИТЬ ВТ_ВсеГруппы
ИЗ
    Справочник.Номенклатура КАК Номенклатура
ГДЕ
    Номенклатура.Ссылка В ИЕРАРХИИ(&КорневаяГруппа)
    И Номенклатура.ЭтоГруппа = ИСТИНА

В этом запросе мы используем оператор В ИЕРАРХИИ, чтобы получить все папки на любую глубину вложенности. Результат сохраняем во временную таблицу ВТ_ВсеГруппы.

Использование АВТОНОМЕРЗАПИСИ для нумерации строк

Поскольку в классическом языке запросов 1С (до версии платформы 8.3.20) нет встроенной функции «получить первые N в группе», мы можем использовать хитрость с функцией АВТОНОМЕРЗАПИСИ(). Эта функция доступна только при помещении данных во временную таблицу. Идея заключается в том, чтобы пронумеровать все товары, предварительно упорядочив их по группам.

Рассмотрим последовательность действий:

  1. Выбираем все элементы номенклатуры, которые принадлежат найденным группам.
  2. Упорядочиваем их по родителю (группе) и ссылке.
  3. Помещаем их во временную таблицу с использованием АВТОНОМЕРЗАПИСИ().

ВЫБРАТЬ
    Номенклатура.Родитель КАК Группа,
    Номенклатура.Ссылка КАК Товар,
    АВТОНОМЕРЗАПИСИ() КАК НомерСтроки
ПОМЕСТИТЬ ВТ_НоменклатураСНомерами
ИЗ
    Справочник.Номенклатура КАК Номенклатура
ГДЕ
    Номенклатура.Родитель В (ВЫБРАТЬ Ссылка ИЗ ВТ_ВсеГруппы)
    И Номенклатура.ЭтоГруппа = ЛОЖЬ
    И Номенклатура.ПометкаУдаления = ЛОЖЬ
УПОРЯДОЧИТЬ ПО
    Группа,
    Товар

Теперь у нас есть таблица, где все товары идут по порядку групп, и каждая строка имеет свой уникальный номер. Однако этот номер сквозной по всей таблице, а не начинается заново для каждой группы. Выясним, как это исправить.

Вычисление смещения и фильтрация по 10 элементов

Чтобы получить «первые 10 в каждой группе», нам нужно знать минимальный номер записи для каждой группы в нашей временной таблице ВТ_НоменклатураСНомерами. Разберем по шагам дальнейшую логику:

  1. Создаем таблицу с минимальными номерами для каждой группы.
  2. Соединяем основную таблицу с этой вспомогательной таблицей по полю группы.
  3. Вычисляем разницу между текущим номером записи и минимальным номером в группе. Если разница меньше 10 — элемент нам подходит.

ВЫБРАТЬ
    ВТ.Группа КАК Группа,
    МИНИМУМ(ВТ.НомерСтроки) КАК НачальныйНомер
ПОМЕСТИТЬ ВТ_СтартыГрупп
ИЗ
    ВТ_НоменклатураСНомерами КАК ВТ
ГРУППИРОВАТЬ ПО
    ВТ.Группа;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ВТ.Группа,
    ВТ.Товар
ИЗ
    ВТ_НоменклатураСНомерами КАК ВТ
    ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_СтартыГрупп КАК Старты
    ПО ВТ.Группа = Старты.Группа
ГДЕ
    ВТ.НомерСтроки МЕЖДУ Старты.НачальныйНомер И Старты.НачальныйНомер + 9

Этот метод позволяет за один проход получить нужные данные. Мы используем условие МЕЖДУ, чтобы ограничить выборку диапазоном от первого найденного номера до десятого.

Особенности реализации в УТ 11

При работе с конфигурацией Управление торговлей 11 недостаточно просто выбрать ссылку на номенклатуру. Документ «Приобретение товаров и услуг» требует заполнения дополнительных реквизитов. Рассмотрим, что еще нужно учесть:

Современный подход: Оконные функции (Платформа 8.3.20+)

Если ваша база работает на современной версии платформы, решение задачи значительно упрощается. Нам больше не нужны три временные таблицы и манипуляции с АВТОНОМЕРЗАПИСИ. Мы можем использовать функцию ПОРЯДКОВЫЙ_НОМЕР().

Рассмотрим пример запроса с оконной функцией:


ВЫБРАТЬ
    Номенклатура.Ссылка КАК Товар,
    Номенклатура.Родитель КАК Группа,
    ПОРЯДКОВЫЙ_НОМЕР() ОБЪЕМЛЮЩИЙ (
        РАЗДЕЛИТЬ ПО Номенклатура.Родитель 
        УПОРЯДОЧИТЬ ПО Номенклатура.Ссылка
    ) КАК НомерВГруппе
ПОМЕСТИТЬ ВТ_Итоговая
ИЗ
    Справочник.Номенклатура КАК Номенклатура
ГДЕ
    Номенклатура.Родитель В ИЕРАРХИИ(&КорневаяГруппа)
    И Номенклатура.ЭтоГруппа = ЛОЖЬ;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ВТ.Товар,
    ВТ.Группа
ИЗ
    ВТ_Итоговая КАК ВТ
ГДЕ
    ВТ.НомерВГруппе <= 10

Использование конструкции РАЗДЕЛИТЬ ПО позволяет платформе автоматически сбрасывать счетчик при смене родителя, что идеально подходит для нашей задачи. Это наиболее производительный и читаемый вариант кода.

Финальные рекомендации

При создании обработки не забудьте добавить на форму возможность выбора Контрагента и Организации, так как они являются обязательными для документа ПриобретениеТоваровИУслуг. После выполнения запроса в коде обработки вам останется только создать объект документа и заполнить его табличную часть в цикле по результату запроса:


НовыйДокумент = Документы.ПриобретениеТоваровИУслуг.СоздатьДокумент();
НовыйДокумент.Дата = ТекущаяДата();
НовыйДокумент.Организация = Объект.Организация;
НовыйДокумент.Поставщик = Объект.Контрагент;
// ... заполнение остальных реквизитов

Для Каждого СтрокаРезультата Из РезультатЗапроса Цикл
    НоваяСтрока = НовыйДокумент.Товары.Добавить();
    НоваяСтрока.Номенклатура = СтрокаРезультата.Товар;
    НоваяСтрока.Количество = 1; // Или другое дефолтное значение
КонецЦикла;

НовыйДокумент.Записать();

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

← На главную