Порядок строк в ТаблицеЗначений: метод, запрос и свой ключ

· 10 мин

Содержание

Отсортировать таблицу значений в 1С проще всего одной строкой: ТЗ.Сортировать("Сумма Убыв, Дата"). Метод меняет порядок строк прямо в этой таблице и ничего не возвращает. Колонки перечисляют через запятую, направление пишут после имени: Возр (по умолчанию) или Убыв.

Когда метода мало, есть ещё два способа. Запрос с УПОРЯДОЧИТЬ ПО нужен, если сортировать надо по реквизиту ссылки или по выражению. Вспомогательная колонка с вычисленным ключом нужна для «своего» порядка: статусы по важности, номера вида «2, 10, 11» как числа, даты без времени.

СпособКогда выбиратьОграничения
Сортировать()Обычная сортировка по колонкамСсылки без СравнениеЗначений идут не по алфавиту
Сортировать() + Новый СравнениеЗначенийНужен алфавитный порядок по ссылкамНа больших таблицах заметно медленнее
Запрос через временную таблицуСортировка по реквизиту ссылки, по выражению, вместе с отбором и группировкойТолько на сервере, у колонок должны быть заданы типы
Вспомогательная колонкаНестандартный порядок, «естественная» сортировка строкЛишняя колонка, её потом удаляют

Метод Сортировать(): синтаксис и примеры сортировки таблицы значений

Синтаксис такой:

ТаблицаЗначений.Сортировать(<Колонки>, <ОбъектСравнения>);
  • 📌 Колонки — строка с именами колонок через запятую. После каждого имени через пробел можно указать направление: Возр или Убыв (в англоязычном варианте Asc / Desc).
  • 📌 ОбъектСравнения — необязательный параметр типа СравнениеЗначений. Влияет на то, как сравниваются ссылочные значения.

Простой пример

ТЗ = Новый ТаблицаЗначений;
ТЗ.Колонки.Добавить("Товар", Новый ОписаниеТипов("Строка"));
ТЗ.Колонки.Добавить("Количество", Новый ОписаниеТипов("Число"));
ТЗ.Колонки.Добавить("Сумма", Новый ОписаниеТипов("Число"));

Стр = ТЗ.Добавить(); Стр.Товар = "Бумага А4"; Стр.Количество = 10; Стр.Сумма = 3500;
Стр = ТЗ.Добавить(); Стр.Товар = "Ручка";     Стр.Количество = 50; Стр.Сумма = 1500;
Стр = ТЗ.Добавить(); Стр.Товар = "Степлер";   Стр.Количество = 5;  Стр.Сумма = 3500;

// Сначала самые дорогие позиции, при равной сумме — по названию
ТЗ.Сортировать("Сумма Убыв, Товар Возр");

Результат: «Бумага А4» (3500), «Степлер» (3500), «Ручка» (1500). У первых двух сумма одинаковая, поэтому дальше сработала вторая колонка: «Б» раньше «С».

Несколько колонок и направление

Порядок колонок в строке — это приоритет. "Склад, Дата Убыв" значит: сгруппировать по складу по возрастанию, а внутри склада поставить свежие даты наверх.

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

Ссылки и СравнениеЗначений

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

Чтобы элементы встали по представлению, передайте объект сравнения:

ТЗ.Сортировать("Номенклатура", Новый СравнениеЗначений);

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

Индексы таблицы значений: что они ускоряют, а что нет

Частое заблуждение: «добавлю индекс, и таблица отсортируется». Индекс не меняет порядок строк. Он ускоряет поиск методами Найти() и НайтиСтроки(), а обход через Для Каждого по-прежнему идёт в порядке строк таблицы.

ТЗ.Индексы.Добавить("Контрагент, Договор");

Отбор = Новый Структура("Контрагент, Договор", Контрагент, Договор);
НайденныеСтроки = ТЗ.НайтиСтроки(Отбор);

Когда индекс полезен:

  • 🔍 в цикле по одной таблице вы много раз ищете строки в другой таблице по одним и тем же колонкам;
  • 🔍 таблица большая (тысячи строк), а поисков сотни и больше;
  • 🔍 набор колонок в отборе совпадает с набором колонок индекса.

Когда индекс лишний:

  • ⏳ поиск выполняется один-два раза: построение индекса стоит дороже;
  • ⏳ таблица постоянно меняется в цикле: индекс поддерживается при каждом изменении;
  • ⏳ вам на самом деле нужен порядок вывода, а не поиск — тогда нужен Сортировать().

Индекс разумно добавлять после того, как таблица заполнена, и перед серией поисков. Так платформа не тратит время на его поддержку во время заполнения.

Сортировка и индекс хорошо работают в паре. Отсортировали таблицу для вывода в печатную форму, а для сопоставления строк с другой таблицей повесили индекс на ключевые колонки.

Сортировка таблицы значений запросом: УПОРЯДОЧИТЬ ПО

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

Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
|	Т.Номенклатура КАК Номенклатура,
|	Т.Количество КАК Количество,
|	Т.Сумма КАК Сумма
|ПОМЕСТИТЬ ВТ_Данные
|ИЗ
|	&ТаблицаДанных КАК Т
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
|	ВТ_Данные.Номенклатура КАК Номенклатура,
|	ВТ_Данные.Количество КАК Количество,
|	ВТ_Данные.Сумма КАК Сумма
|ИЗ
|	ВТ_Данные КАК ВТ_Данные
|
|УПОРЯДОЧИТЬ ПО
|	ВТ_Данные.Номенклатура.Наименование,
|	Сумма УБЫВ";

Запрос.УстановитьПараметр("ТаблицаДанных", ТЗ);
ТЗ = Запрос.Выполнить().Выгрузить();

Что здесь происходит:

  1. Первый пакет кладёт переданную таблицу во временную ВТ_Данные. Таблицу-параметр в запросе можно только поместить во временную таблицу, поэтому этот шаг обязателен.
  2. Второй пакет читает временную таблицу и сортирует по наименованию номенклатуры. Обращение ВТ_Данные.Номенклатура.Наименование подтягивает реквизит из справочника.
  3. Выгрузить() возвращает новую, уже упорядоченную таблицу значений.

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

Плюсы запроса — в одном месте можно и отобрать (ГДЕ), и сгруппировать (СГРУППИРОВАТЬ ПО), и упорядочить. Минус — запрос выполняется на сервере, а таблица уходит туда целиком. Для таблицы в 200 строк, которую можно отсортировать по существующим колонкам, это лишние накладные расходы: проще Сортировать().

Свой порядок сортировки, когда стандартного метода мало

Метод Сортировать() умеет сравнивать только значения колонок «как есть». Если порядок задаётся правилом, сделайте колонку-ключ, отсортируйте по ней и удалите её.

Порядок статусов по важности

Нужно, чтобы сверху шли «Срочно», потом «В работе», потом «Ожидает», а остальное — в конце. По алфавиту так не получится.

Приоритет = Новый Соответствие;
Приоритет.Вставить("Срочно", 1);
Приоритет.Вставить("В работе", 2);
Приоритет.Вставить("Ожидает", 3);

ТЗ.Колонки.Добавить("КлючСортировки", Новый ОписаниеТипов("Число"));

Для Каждого Стр Из ТЗ Цикл
	Вес = Приоритет[Стр.Статус];
	Стр.КлючСортировки = ?(Вес = Неопределено, 99, Вес);
КонецЦикла;

ТЗ.Сортировать("КлючСортировки, Дата Убыв");
ТЗ.Колонки.Удалить("КлючСортировки");

Неизвестный статус получает вес 99 и уходит вниз, а не вызывает ошибку.

Номера-строки как числа

В колонке строкового типа значения «2», «10», «11» сортируются как текст: «10», «11», «2». Пользователь ждёт обратного. Решение — числовой ключ:

ТЗ.Колонки.Добавить("НомерЧислом", Новый ОписаниеТипов("Число"));
ОписаниеЧисла = Новый ОписаниеТипов("Число");

Для Каждого Стр Из ТЗ Цикл
	Стр.НомерЧислом = ОписаниеЧисла.ПривестиЗначение(СокрЛП(Стр.Номер));
КонецЦикла;

ТЗ.Сортировать("НомерЧислом, Номер");
ТЗ.Колонки.Удалить("НомерЧислом");

ПривестиЗначение вернёт 0 для строки, которая не является числом, так что такие строки окажутся в начале. Вторая колонка Номер упорядочит их между собой.

Другие ключи, которые часто нужны

ЗадачаЧто положить в колонку-ключ
Сортировка по дню, без учёта времениНачалоДня(Стр.Дата)
Сортировка без учёта регистраНРег(Стр.Наименование)
По длине строкиСтрДлина(Стр.Текст)
По модулю суммы?(Стр.Сумма < 0, -Стр.Сумма, Стр.Сумма)
По месяцу, затем по контрагентуНачалоМесяца(Стр.Дата) и колонка контрагента

Если вспомогательная колонка нужна ещё для отбора или группировки, не удаляйте её сразу. Удаляйте перед тем, как отдать таблицу дальше: в макет, на форму или в другой модуль.

Сортировка после группировки: Свернуть() и итоги по группам

Типовая задача: из списка движений получить итоги по контрагентам и показать самых крупных сверху. Сначала сворачиваем, потом сортируем:

// Колонки группировки — первым параметром, суммируемые — вторым
ТЗ.Свернуть("Контрагент", "Сумма, Количество");
ТЗ.Сортировать("Сумма Убыв");

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

Детальные строки внутри групп

Если нужны не итоги, а детальные строки, упорядоченные внутри каждой группы, достаточно одной сортировки по нескольким колонкам:

ТЗ.Сортировать("Контрагент, Сумма Убыв");

ТекущийКонтрагент = Неопределено;
Для Каждого Стр Из ТЗ Цикл
	Если Стр.Контрагент <> ТекущийКонтрагент Тогда
		ТекущийКонтрагент = Стр.Контрагент;
		// здесь выводим заголовок группы
	КонецЕсли;
	// здесь выводим строку группы
КонецЦикла;

После такой сортировки строки одного контрагента идут подряд, и смена значения в цикле означает начало новой группы. Если группы нужно ещё и упорядочить по итогу (крупные контрагенты сверху), посчитайте итог в отдельной таблице через Скопировать() и Свернуть(), перенесите его в колонку-ключ и сортируйте по "ИтогКонтрагента Убыв, Контрагент, Сумма Убыв".

Дерево значений

У дерева сортируют коллекцию строк. Второй параметр говорит, нужно ли упорядочить и вложенные уровни:

Дерево.Строки.Сортировать("Сумма Убыв", Истина);

С Ложь отсортируется только верхний уровень, подчинённые строки останутся как были.

Сортировка табличной части и таблицы на форме

На управляемой форме табличная часть документа и реквизит формы типа «Таблица значений» хранятся не как ТаблицаЗначений, а как ДанныеФормыКоллекция. Надёжный способ — преобразовать в объект на сервере, отсортировать и вернуть:

&НаСервере
Процедура УпорядочитьТоварыНаСервере()
	ДокументОбъект = РеквизитФормыВЗначение("Объект");
	ДокументОбъект.Товары.Сортировать("Номенклатура", Новый СравнениеЗначений);
	ЗначениеВРеквизитФормы(ДокументОбъект, "Объект");
КонецПроцедуры

У табличной части объекта метод Сортировать() есть, синтаксис тот же. Номера строк после сортировки пересчитываются сами. Для реквизита формы с таблицей значений схема та же: РеквизитФормыВЗначение("ИмяРеквизита"), сортировка, ЗначениеВРеквизитФормы.

После сортировки табличной части документ становится изменённым. Если пользователь закроет форму без записи, порядок не сохранится — это ожидаемое поведение, а не сбой.

Какой способ сортировки выбрать?

Как сортировать большие таблицы значений без потери скорости

На десятках тысяч строк сам Сортировать() по числам и датам работает быстро. Тормозит обычно то, что вокруг него.

  1. Сортируйте один раз. Вызов Сортировать() внутри цикла, после каждой добавленной строки, — главная причина медленного кода. Заполните таблицу целиком, потом упорядочьте.
  2. Сокращайте таблицу до сортировки. ТЗ.Скопировать(Отбор, "Колонка1, Колонка2") вернёт только нужные строки и колонки. Меньше данных — быстрее и сортировка, и передача на клиент.
  3. Избегайте СравнениеЗначений на больших объёмах. Подготовьте строковую колонку с наименованием и сортируйте по ней.
  4. Не гоняйте таблицу в запрос ради сортировки. Если данные изначально пришли из запроса, сразу пишите УПОРЯДОЧИТЬ ПО в нём, а не выгружайте и сортируйте потом.
  5. Не путайте сортировку с поиском. Нужен быстрый поиск — индекс и НайтиСтроки(). Сортировка ради поиска «глазами» в цикле ничего не ускоряет.
  6. Ключ считайте один раз. Вычисляемую колонку заполняйте одним проходом, а не пересчитывайте в нескольких местах.

Замерить, что именно тормозит, удобно встроенным замером производительности в отладчике Конфигуратора. Часто оказывается, что время уходит не на сортировку, а на обращение к реквизитам ссылок при заполнении ключа.

Ошибки при сортировке и как их исправить

Опечатка в имени колонки. Строка колонок проверяется при выполнении, поэтому ошибка всплывёт только в рантайме. Скопируйте имя из ТЗ.Колонки.Добавить(...), а не набирайте заново. Сверьте и пробелы: между колонками — запятая, между именем и направлением — пробел.

Сортировка результата, а не таблицы. Код ТЗ2 = ТЗ.Сортировать("Сумма") записывает в ТЗ2 значение Неопределено, потому что метод ничего не возвращает. Правильно: сначала ТЗ.Сортировать("Сумма"), потом работать с ТЗ.

Ссылки «вразнобой». Это не ошибка платформы, а сортировка по внутреннему значению ссылки. Добавьте Новый СравнениеЗначений или сортируйте по наименованию.

Числа в строковой колонке. «10» стоит перед «2». Нужен числовой ключ, пример выше.

Разные типы в одной колонке. Колонка без типа, куда попали и числа, и строки, и Неопределено, сортируется не так, как ждёт пользователь. Задайте колонке тип при создании или приведите значения к одному типу перед сортировкой.

Ошибка в запросе на нетипизированной колонке. Колонке нужен тип через ОписаниеТипов. Если таблицу собирают другие модули, создайте новую колонку с типом и перенесите в неё значения.

Порядок «сломался» после изменения строк. Сортировка разовая: добавленные потом строки встают в конец, а после Свернуть() порядок не гарантирован. Пересортируйте таблицу перед выводом.

Частые вопросы о сортировке таблицы значений

Как отсортировать массив в 1С?
У массива нет метода сортировки. Загрузите значения в СписокЗначений методом ЗагрузитьЗначения(), вызовите СортироватьПоЗначению() и выгрузите обратно через ВыгрузитьЗначения().
Можно ли сортировать таблицу значений на клиенте?
Объект ТаблицаЗначений доступен только на сервере и в толстом клиенте. В тонком клиенте сортируйте на сервере в процедуре с &НаСервере, а на форме храните результат в реквизите формы.
Есть ли у таблицы значений «обратный» метод, чтобы просто перевернуть порядок?
Отдельного метода нет. Если нужен обратный порядок по той же колонке, отсортируйте с Убыв. Если ключа нет, добавьте колонку с номером строки и отсортируйте по ней с Убыв.
Сохраняется ли сортировка при Скопировать()?
Скопированная таблица получает строки в том порядке, в котором они шли в исходной. Отсортируйте исходную до копирования или копию после.
Чем сортировка в СКД отличается от сортировки таблицы значений?
В отчёте на СКД порядок задают в настройках варианта отчёта, на вкладке сортировки. Если таблица значений служит внешним набором данных, сортировать её в коде перед выводом не нужно: схема всё равно применит свой порядок.