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

Программист 1С v8.3 (Обычные формы) IT и автоматизация бизнеса
← На главную

При разработке алгоритмов заполнения документов на основании или при выгрузке данных разработчики часто обращаются к документу-источнику единым запросом, выбирая шапку и вложенные табличные части через синтаксис Документ.Товары.(...). Однако при попытке упорядочить строки внутри полученных коллекций возникает проблема: стандартная конструкция УПОРЯДОЧИТЬ ПО сортирует только строки верхнего уровня (документы), оставляя строки во вложенных таблицах в исходном или случайном порядке.

Разберем причины такого поведения платформы «1С:Предприятие» и рассмотрим несколько практических способов решения данной задачи.

Почему не работает стандартное упорядочивание во вложенных таблицах

Проанализируем исходный запрос, в котором возникло затруднение:


ВЫБРАТЬ РАЗРЕШЕННЫЕ
    Док.СуммаВключаетНДС,
    Док.ВалютаДокумента,
    Док.Контрагент,
    Док.ТипЦен КАК ТипЦен,
    Док.Товары.(
        Номенклатура,
        Цена КАК Цена,
        СтавкаНДС,
        СУММА(НомерСтроки) КАК НомерСтроки
    ),
    Док.Услуги.(
        СУММА(НомерСтроки) КАК НомерСтроки,
        Номенклатура,
        Цена,
        СтавкаНДС
    )
ИЗ
    Документ.ПоступлениеТоваровУслуг КАК Док
ГДЕ
    Док.Ссылка = &ДокументОснование
СГРУППИРОВАТЬ ПО
    Док.Товары.(Номенклатура, Цена, СтавкаНДС),
    Док.Услуги.(Номенклатура, Цена, СтавкаНДС)

Выясним причину проблемы. В языке запросов 1С предложение УПОРЯДОЧИТЬ ПО управляет сортировкой только записей основного уровня выборки. Конструкция вложенных таблиц внутри секции ВЫБРАТЬ формирует вложенные выборки, на внутренний порядок которых внешняя директива сортировки не оказывает прямого влияния.

Важное замечание: агрегация вида СУММА(НомерСтроки) при группировке строк искажает реальные порядковые номера (например, при свертке строк с номерами 2 и 3 результирующий номер станет равен 5). Если требуется сохранить наименьший номер для правильного позиционирования, правильнее применять агрегатную функцию МИНИМУМ(НомерСтроки).

Способ 1. Использование вложенного запроса с внешним упорядочиванием

Рассмотрим вариант обхода ограничения, при котором группировка выполняется во вложенном подзапросе, а во внешнем запросе явно задается сортировка по полям вложенной таблицы.

Посмотрим на пример реализации:


Запрос = Новый Запрос;
Запрос.УстановитьПараметр("ДокументОснование", Основание);
Запрос.Текст = 
    "ВЫБРАТЬ
    |    Д.Ссылка,
    |    Д.Товары.(
    |        НомерСтроки КАК НомерСтроки,
    |        Номенклатура КАК Номенклатура,
    |        Цена КАК Цена,
    |        СтавкаНДС КАК СтавкаНДС
    |    ),
    |    Д.Услуги.(
    |        НомерСтроки КАК НомерСтроки,
    |        Номенклатура КАК Номенклатура,
    |        Цена КАК Цена,
    |        СтавкаНДС КАК СтавкаНДС
    |    ),
    |    Д.Ссылка.СуммаВключаетНДС КАК СуммаВключаетНДС,
    |    Д.Ссылка.ВалютаДокумента КАК ВалютаДокумента,
    |    Д.Ссылка.Контрагент КАК Контрагент,
    |    Д.Ссылка.ТипЦен КАК ТипЦен
    |ИЗ
    |    (ВЫБРАТЬ
    |        Поступление.Ссылка КАК Ссылка,
    |        Поступление.Товары.(
    |            МИНИМУМ(НомерСтроки) КАК НомерСтроки,
    |            Номенклатура КАК Номенклатура,
    |            Цена КАК Цена,
    |            СтавкаНДС КАК СтавкаНДС
    |        ) КАК Товары,
    |        Поступление.Услуги.(
    |            МИНИМУМ(НомерСтроки) КАК НомерСтроки,
    |            Номенклатура КАК Номенклатура,
    |            Цена КАК Цена,
    |            СтавкаНДС КАК СтавкаНДС
    |        ) КАК Услуги
    |    ИЗ
    |        Документ.ПоступлениеТоваровУслуг КАК Поступление
    |    ГДЕ
    |        Поступление.Ссылка = &ДокументОснование
    |    СГРУППИРОВАТЬ ПО
    |        Поступление.Товары.(Номенклатура, Цена, СтавкаНДС),
    |        Поступление.Услуги.(Номенклатура, Цена, СтавкаНДС)
    |    ) КАК Д
    |
    |УПОРЯДОЧИТЬ ПО
    |    НомерСтроки";

РезультатЗапроса = Запрос.Выполнить();

Следует учитывать, что синтаксис вложенных таблиц в секции ВЫБРАТЬ считается устаревшим (deprecated) в стандартах разработки 1С, так как трансляция таких конструкций в SQL-код может порождать неоптимальные планы выполнения в СУБД. Поэтому разберем более надежные архитектурные подходы.

Способ 2. Пакетный запрос к реальным табличным частям (рекомендуемый стандарт)

Наиболее надежный и производительный способ — разделение выборки на пакет независимых запросов. Мы обращаемся напрямую к физическим таблицам табличных частей Документ.ПоступлениеТоваровУслуг.Товары и Документ.ПоступлениеТоваровУслуг.Услуги, где сортировка УПОРЯДОЧИТЬ ПО работает гарантированно и без побочных эффектов.

Разберем по шагам построение пакетного запроса:

  1. Первым запросом пакета получаем реквизиты шапки документа.
  2. Вторым запросом выбираем строки табличной части товаров с необходимой группировкой и явным упорядочиванием по номеру строки.
  3. Третьим запросом выбираем строки табличной части услуг аналогичным образом.
  4. Выполняем пакет через метод ВыполнитьПакет().

Посмотрим на пример программного кода:


Запрос = Новый Запрос;
Запрос.УстановитьПараметр("ДокументОснование", Основание);
Запрос.Текст = 
    "ВЫБРАТЬ РАЗРЕШЕННЫЕ
    |    Док.СуммаВключаетНДС КАК СуммаВключаетНДС,
    |    Док.ВалютаДокумента КАК ВалютаДокумента,
    |    Док.Контрагент КАК Контрагент,
    |    Док.ТипЦен КАК ТипЦен
    |ИЗ
    |    Документ.ПоступлениеТоваровУслуг КАК Док
    |ГДЕ
    |    Док.Ссылка = &ДокументОснование
    |;
    |
    |////////////////////////////////////////////////////////////////////////////////
    |ВЫБРАТЬ РАЗРЕШЕННЫЕ
    |    МИНИМУМ(Товары.НомерСтроки) КАК НомерСтроки,
    |    Товары.Номенклатура КАК Номенклатура,
    |    Товары.Цена КАК Цена,
    |    Товары.СтавкаНДС КАК СтавкаНДС
    |ИЗ
    |    Документ.ПоступлениеТоваровУслуг.Товары КАК Товары
    |ГДЕ
    |    Товары.Ссылка = &ДокументОснование
    |СГРУППИРОВАТЬ ПО
    |    Товары.Номенклатура,
    |    Товары.Цена,
    |    Товары.СтавкаНДС
    |УПОРЯДОЧИТЬ ПО
    |    НомерСтроки ВОЗР
    |;
    |
    |////////////////////////////////////////////////////////////////////////////////
    |ВЫБРАТЬ РАЗРЕШЕННЫЕ
    |    МИНИМУМ(Услуги.НомерСтроки) КАК НомерСтроки,
    |    Услуги.Номенклатура КАК Номенклатура,
    |    Услуги.Цена КАК Цена,
    |    Услуги.СтавкаНДС КАК СтавкаНДС
    |ИЗ
    |    Документ.ПоступлениеТоваровУслуг.Услуги КАК Услуги
    |ГДЕ
    |    Услуги.Ссылка = &ДокументОснование
    |СГРУППИРОВАТЬ ПО
    |    Услуги.Номенклатура,
    |    Услуги.Цена,
    |    Услуги.СтавкаНДС
    |УПОРЯДОЧИТЬ ПО
    |    НомерСтроки ВОЗР";

МассивРезультатов = Запрос.ВыполнитьПакет();

Шапка = МассивРезультатов[0].Выбрать();
Если Шапка.Следующий() Тогда
    // Заполняем реквизиты шапки
КонецЕсли;

ТЗТовары = МассивРезультатов[1].Выгрузить();
ТЗУслуги = МассивРезультатов[2].Выгрузить();

Способ 3. Программная сортировка полученной ТаблицыЗначений

Если в конфигурации уже используется механизм выборки во вложенную коллекцию и изменение структуры запроса нежелательно, можно воспользоваться методом Сортировать() объекта ТаблицаЗначений.

Рассмотрим, как выполнить постобработку в коде:


Шапка = Запрос.Выполнить().Выбрать();
Если Шапка.Следующий() Тогда
    ТаблицаТовары = Шапка.Товары.Выгрузить();
    ТаблицаТовары.Сортировать("НомерСтроки Возр");
    
    Для Каждого Стр Из ТаблицаТовары Цикл
        // Обработка отсортированных строк
    КонецЦикла;
КонецЕсли;

Метод ТаблицаЗначений.Сортировать() выполняется в оперативной памяти платформы практически мгновенно для типичных объемов документов и гарантирует строгий порядок строк.

← На главную