Почему не работает сортировка табличной части по номеру строки и как это исправить?

Программист 1С:Управление торговлей IT и автоматизация бизнеса
← На главную

В процессе разработки в среде 1С:Предприятие 8.3 часто возникает необходимость отсортировать табличную часть документа или обработки по определенному реквизиту (например, КлючСвязи), сохранив при этом исходный порядок следования строк внутри групп. На первый взгляд, решение кажется очевидным: вызвать метод Сортировать("КлючСвязи, НомерСтроки"). Однако на практике программисты сталкиваются с тем, что строки меняются местами непредсказуемым образом, а системное поле НомерСтроки игнорируется или ведет себя некорректно.

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

Выясним причину: почему НомерСтроки не работает в методе Сортировать()

Разберем ситуацию, с которой столкнулся автор темы. При попытке выполнить код ТЧ.Сортировать("КлючСвязи, НомерСтроки") платформа не выдает ошибку (в новых версиях), но и не выполняет ожидаемую сортировку. Этому есть несколько технических объяснений:

  1. Динамическая природа поля: Поле НомерСтроки является системным и вычисляемым. Оно жестко привязано к текущему индексу строки в коллекции. Как только алгоритм сортировки перемещает строку, её НомерСтроки тут же меняется. Попытка сортировать по значению, которое меняется прямо в процессе сортировки, приводит к логической ошибке.
  2. Ограничения метода: Объект ТабличнаяЧасть имеет встроенный метод Сортировать(), который официально предназначен для работы с измерениями (в случае регистров) или реквизитами. Системные поля, такие как НомерСтроки, не входят в список доступных колонок для этого метода, даже если платформа «молча» пропускает такой параметр.
  3. Нестабильность алгоритма: Алгоритм сортировки, применяемый в 1С, не является стабильным. Это означает, что если значения в колонках сортировки (например, в поле КлючСвязи) совпадают, относительный порядок строк не гарантируется. Без указания дополнительного статического уникального поля строки будут перемешаны.

Решение №1: Использование вспомогательной колонки в Таблице Значений

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

Проанализируем шаги выполнения этой задачи:

  1. Выгрузим табличную часть в таблицу значений.
  2. Добавим в таблицу значений новую колонку (например, ИсходныйПорядок) с типом Число.
  3. Заполним её текущими индексами строк.
  4. Выполним сортировку по нужному полю и нашей новой колонке.
  5. Загрузим результат обратно в табличную часть.

Посмотрим на пример кода, реализующего этот алгоритм:


// Выгружаем ТЧ в таблицу значений
ТЗ = Объект.Товары.Выгрузить();

// Добавляем колонку для фиксации порядка
ТЗ.Колонки.Добавить("ИсходныйПорядок", Новый ОписаниеТипов("Число"));

// Заполняем колонку
Для Каждого СтрокаТЗ Из ТЗ Цикл
    СтрокаТЗ.ИсходныйПорядок = ТЗ.Индекс(СтрокаТЗ);
КонецЦикла;

// Сортируем по ключу и нашему сохраненному порядку
ТЗ.Сортировать("КлючСвязи Возр, ИсходныйПорядок Возр");

// Загружаем данные обратно в ТЧ
Объект.Товары.Загрузить(ТЗ);

Важный момент: Метод Загрузить() полностью очищает текущую табличную часть и заполняет её строками из таблицы значений, строго соблюдая их порядок. Это штатное поведение платформы, которое гарантирует нужный нам результат.

Решение №2: Использование запроса и метода Сдвинуть()

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

Разберем пример кода, предложенный автором темы, где используется запрос к табличной части и последующее перемещение строк методом Сдвинуть():


Процедура СортировкаТЧПоПолюВПорядкеСледования(ТЧ, ИмяПоляСортировки)
    
    Запрос = Новый Запрос;
    // Передаем табличную часть как параметр запроса
    Запрос.УстановитьПараметр("ТЧ", ТЧ);
    
    Запрос.Текст = 
        "ВЫБРАТЬ
        |    ТЧ.НомерСтроки КАК НомерСтроки,
        |    ТЧ." + ИмяПоляСортировки + " КАК ПолеСортировки
        |ПОМЕСТИТЬ ВТ_Данные
        |ИЗ
        |    &ТЧ КАК ТЧ
        |;
        |ВЫБРАТЬ
        |    ВТ_Данные.НомерСтроки
        |ИЗ
        |    ВТ_Данные КАК ВТ_Данные
        |УПОРЯДОЧИТЬ ПО
        |    ПолеСортировки,
        |    НомерСтроки";
        
    РезультатЗапроса = Запрос.Выполнить();
    Выборка = РезультатЗапроса.Выбрать();
    
    ТекИндекс = 0;
    Пока Выборка.Следующий() Цикл
        // Находим строку по её старому номеру (индексу)
        // Внимание: этот метод требует осторожности при больших объемах данных
        СтрокаТЧ = ТЧ.Получить(Выборка.НомерСтроки - 1);
        Сдвиг = ТекИндекс - ТЧ.Индекс(СтрокаТЧ);
        
        Если Сдвиг <> 0 Тогда 
            ТЧ.Сдвинуть(СтрокаТЧ, Сдвиг);
        КонецЕсли;
        
        ТекИндекс = ТекИндекс + 1;
    КонецЦикла;
    
КонецПроцедуры

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

Подведем итоги

Мы выяснили, что стандартная сортировка табличной части по НомерСтроки невозможна из-за ограничений платформы 1С:Предприятие 8. Для реализации устойчивой сортировки (stable sort) следует использовать один из двух подходов:

Помните, что поле НомерСтроки в запросе — это число, а в методе Сортировать() — динамический указатель, который нельзя использовать как ключ сортировки.

← На главную