Порядок строк в ТаблицеЗначений: метод, запрос и свой ключ
Содержание
Отсортировать таблицу значений в 1С проще всего одной строкой: ТЗ.Сортировать("Сумма Убыв, Дата"). Метод меняет порядок строк прямо в этой таблице и ничего не возвращает. Колонки перечисляют через запятую, направление пишут после имени: Возр (по умолчанию) или Убыв.
Когда метода мало, есть ещё два способа. Запрос с УПОРЯДОЧИТЬ ПО нужен, если сортировать надо по реквизиту ссылки или по выражению. Вспомогательная колонка с вычисленным ключом нужна для «своего» порядка: статусы по важности, номера вида «2, 10, 11» как числа, даты без времени.
| Способ | Когда выбирать | Ограничения |
|---|---|---|
Сортировать() | Обычная сортировка по колонкам | Ссылки без СравнениеЗначений идут не по алфавиту |
Сортировать() + Новый СравнениеЗначений | Нужен алфавитный порядок по ссылкам | На больших таблицах заметно медленнее |
| Запрос через временную таблицу | Сортировка по реквизиту ссылки, по выражению, вместе с отбором и группировкой | Только на сервере, у колонок должны быть заданы типы |
| Вспомогательная колонка | Нестандартный порядок, «естественная» сортировка строк | Лишняя колонка, её потом удаляют |
Метод Сортировать(): синтаксис и примеры сортировки таблицы значений
Синтаксис такой:
ТаблицаЗначений.Сортировать(<Колонки>, <ОбъектСравнения>);
- 📌 Колонки — строка с именами колонок через запятую. После каждого имени через пробел можно указать направление:
ВозрилиУбыв(в англоязычном вариантеAsc/Desc). - 📌 ОбъектСравнения — необязательный параметр типа
СравнениеЗначений. Влияет на то, как сравниваются ссылочные значения.
Простой пример
ТЗ = Новый ТаблицаЗначений;
ТЗ.Колонки.Добавить("Товар", Новый ОписаниеТипов("Строка"));
ТЗ.Колонки.Добавить("Количество", Новый ОписаниеТипов("Число"));
ТЗ.Колонки.Добавить("Сумма", Новый ОписаниеТипов("Число"));
Стр = ТЗ.Добавить(); Стр.Товар = "Бумага А4"; Стр.Количество = 10; Стр.Сумма = 3500;
Стр = ТЗ.Добавить(); Стр.Товар = "Ручка"; Стр.Количество = 50; Стр.Сумма = 1500;
Стр = ТЗ.Добавить(); Стр.Товар = "Степлер"; Стр.Количество = 5; Стр.Сумма = 3500;
// Сначала самые дорогие позиции, при равной сумме — по названию
ТЗ.Сортировать("Сумма Убыв, Товар Возр");
Результат: «Бумага А4» (3500), «Степлер» (3500), «Ручка» (1500). У первых двух сумма одинаковая, поэтому дальше сработала вторая колонка: «Б» раньше «С».
Несколько колонок и направление
Порядок колонок в строке — это приоритет. "Склад, Дата Убыв" значит: сгруппировать по складу по возрастанию, а внутри склада поставить свежие даты наверх.
Если для одинаковых значений вам важен конкретный порядок, допишите в сортировку ещё одну колонку (номер, дату, код). Не стоит полагаться на то, что строки с равным ключом сохранят прежнюю очерёдность.
Ссылки и СравнениеЗначений
Колонка с типом СправочникСсылка.Номенклатура сортируется без второго параметра, но порядок получится не алфавитный: платформа сравнивает ссылки по их внутреннему значению. Пользователь видит «перемешанный» список и пишет в поддержку.
Чтобы элементы встали по представлению, передайте объект сравнения:
ТЗ.Сортировать("Номенклатура", Новый СравнениеЗначений);
Этот вариант проще всего, но на таблицах в десятки тысяч строк он ощутимо медленнее. Быстрее заранее положить наименование в строковую колонку (или получить его запросом) и сортировать уже по ней.
Индексы таблицы значений: что они ускоряют, а что нет
Частое заблуждение: «добавлю индекс, и таблица отсортируется». Индекс не меняет порядок строк. Он ускоряет поиск методами Найти() и НайтиСтроки(), а обход через Для Каждого по-прежнему идёт в порядке строк таблицы.
ТЗ.Индексы.Добавить("Контрагент, Договор");
Отбор = Новый Структура("Контрагент, Договор", Контрагент, Договор);
НайденныеСтроки = ТЗ.НайтиСтроки(Отбор);
Когда индекс полезен:
- 🔍 в цикле по одной таблице вы много раз ищете строки в другой таблице по одним и тем же колонкам;
- 🔍 таблица большая (тысячи строк), а поисков сотни и больше;
- 🔍 набор колонок в отборе совпадает с набором колонок индекса.
Когда индекс лишний:
- ⏳ поиск выполняется один-два раза: построение индекса стоит дороже;
- ⏳ таблица постоянно меняется в цикле: индекс поддерживается при каждом изменении;
- ⏳ вам на самом деле нужен порядок вывода, а не поиск — тогда нужен
Сортировать().
Индекс разумно добавлять после того, как таблица заполнена, и перед серией поисков. Так платформа не тратит время на его поддержку во время заполнения.
Сортировка и индекс хорошо работают в паре. Отсортировали таблицу для вывода в печатную форму, а для сопоставления строк с другой таблицей повесили индекс на ключевые колонки.
Сортировка таблицы значений запросом: УПОРЯДОЧИТЬ ПО
Запрос пригодится, когда сортировать нужно по тому, чего в колонках нет: по наименованию или коду из ссылки, по выражению, по вычисленному полю. Таблица значений передаётся в запрос параметром и сначала помещается во временную таблицу.
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Т.Номенклатура КАК Номенклатура,
| Т.Количество КАК Количество,
| Т.Сумма КАК Сумма
|ПОМЕСТИТЬ ВТ_Данные
|ИЗ
| &ТаблицаДанных КАК Т
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| ВТ_Данные.Номенклатура КАК Номенклатура,
| ВТ_Данные.Количество КАК Количество,
| ВТ_Данные.Сумма КАК Сумма
|ИЗ
| ВТ_Данные КАК ВТ_Данные
|
|УПОРЯДОЧИТЬ ПО
| ВТ_Данные.Номенклатура.Наименование,
| Сумма УБЫВ";
Запрос.УстановитьПараметр("ТаблицаДанных", ТЗ);
ТЗ = Запрос.Выполнить().Выгрузить();
Что здесь происходит:
- Первый пакет кладёт переданную таблицу во временную
ВТ_Данные. Таблицу-параметр в запросе можно только поместить во временную таблицу, поэтому этот шаг обязателен. - Второй пакет читает временную таблицу и сортирует по наименованию номенклатуры. Обращение
ВТ_Данные.Номенклатура.Наименованиеподтягивает реквизит из справочника. Выгрузить()возвращает новую, уже упорядоченную таблицу значений.
Каждая колонка исходной таблицы должна иметь тип, заданный через ОписаниеТипов. Колонка, добавленная без типа, в запрос не передастся: платформа выдаст ошибку о том, что тип не может быть выбран в запросе. Особенно часто это ловят на таблицах, собранных вручную в цикле.
Плюсы запроса — в одном месте можно и отобрать (ГДЕ), и сгруппировать (СГРУППИРОВАТЬ ПО), и упорядочить. Минус — запрос выполняется на сервере, а таблица уходит туда целиком. Для таблицы в 200 строк, которую можно отсортировать по существующим колонкам, это лишние накладные расходы: проще Сортировать().
Свой порядок сортировки, когда стандартного метода мало
Метод Сортировать() умеет сравнивать только значения колонок «как есть». Если порядок задаётся правилом, сделайте колонку-ключ, отсортируйте по ней и удалите её.
Порядок статусов по важности
Нужно, чтобы сверху шли «Срочно», потом «В работе», потом «Ожидает», а остальное — в конце. По алфавиту так не получится.
Приоритет = Новый Соответствие;
Приоритет.Вставить("Срочно", 1);
Приоритет.Вставить("В работе", 2);
Приоритет.Вставить("Ожидает", 3);
ТЗ.Колонки.Добавить("КлючСортировки", Новый ОписаниеТипов("Число"));
Для Каждого Стр Из ТЗ Цикл
Вес = Приоритет[Стр.Статус];
Стр.КлючСортировки = ?(Вес = Неопределено, 99, Вес);
КонецЦикла;
ТЗ.Сортировать("КлючСортировки, Дата Убыв");
ТЗ.Колонки.Удалить("КлючСортировки");
Неизвестный статус получает вес 99 и уходит вниз, а не вызывает ошибку.
Номера-строки как числа
В колонке строкового типа значения «2», «10», «11» сортируются как текст: «10», «11», «2». Пользователь ждёт обратного. Решение — числовой ключ:
ТЗ.Колонки.Добавить("НомерЧислом", Новый ОписаниеТипов("Число"));
ОписаниеЧисла = Новый ОписаниеТипов("Число");
Для Каждого Стр Из ТЗ Цикл
Стр.НомерЧислом = ОписаниеЧисла.ПривестиЗначение(СокрЛП(Стр.Номер));
КонецЦикла;
ТЗ.Сортировать("НомерЧислом, Номер");
ТЗ.Колонки.Удалить("НомерЧислом");
ПривестиЗначение вернёт 0 для строки, которая не является числом, так что такие строки окажутся в начале. Вторая колонка Номер упорядочит их между собой.
Другие ключи, которые часто нужны
| Задача | Что положить в колонку-ключ |
|---|---|
| Сортировка по дню, без учёта времени | НачалоДня(Стр.Дата) |
| Сортировка без учёта регистра | НРег(Стр.Наименование) |
| По длине строки | СтрДлина(Стр.Текст) |
| По модулю суммы | ?(Стр.Сумма < 0, -Стр.Сумма, Стр.Сумма) |
| По месяцу, затем по контрагенту | НачалоМесяца(Стр.Дата) и колонка контрагента |
Если вспомогательная колонка нужна ещё для отбора или группировки, не удаляйте её сразу. Удаляйте перед тем, как отдать таблицу дальше: в макет, на форму или в другой модуль.
Сортировка после группировки: Свернуть() и итоги по группам
Типовая задача: из списка движений получить итоги по контрагентам и показать самых крупных сверху. Сначала сворачиваем, потом сортируем:
// Колонки группировки — первым параметром, суммируемые — вторым
ТЗ.Свернуть("Контрагент", "Сумма, Количество");
ТЗ.Сортировать("Сумма Убыв");
Не рассчитывайте, что Свернуть() вернёт строки в каком-то порядке. Сортируйте явно после свёртки. Все колонки, не указанные ни в одном из параметров Свернуть(), из таблицы удаляются, поэтому колонку для будущей сортировки включите в группировку или в суммирование.
Детальные строки внутри групп
Если нужны не итоги, а детальные строки, упорядоченные внутри каждой группы, достаточно одной сортировки по нескольким колонкам:
ТЗ.Сортировать("Контрагент, Сумма Убыв");
ТекущийКонтрагент = Неопределено;
Для Каждого Стр Из ТЗ Цикл
Если Стр.Контрагент <> ТекущийКонтрагент Тогда
ТекущийКонтрагент = Стр.Контрагент;
// здесь выводим заголовок группы
КонецЕсли;
// здесь выводим строку группы
КонецЦикла;
После такой сортировки строки одного контрагента идут подряд, и смена значения в цикле означает начало новой группы. Если группы нужно ещё и упорядочить по итогу (крупные контрагенты сверху), посчитайте итог в отдельной таблице через Скопировать() и Свернуть(), перенесите его в колонку-ключ и сортируйте по "ИтогКонтрагента Убыв, Контрагент, Сумма Убыв".
Дерево значений
У дерева сортируют коллекцию строк. Второй параметр говорит, нужно ли упорядочить и вложенные уровни:
Дерево.Строки.Сортировать("Сумма Убыв", Истина);
С Ложь отсортируется только верхний уровень, подчинённые строки останутся как были.
Сортировка табличной части и таблицы на форме
На управляемой форме табличная часть документа и реквизит формы типа «Таблица значений» хранятся не как ТаблицаЗначений, а как ДанныеФормыКоллекция. Надёжный способ — преобразовать в объект на сервере, отсортировать и вернуть:
&НаСервере
Процедура УпорядочитьТоварыНаСервере()
ДокументОбъект = РеквизитФормыВЗначение("Объект");
ДокументОбъект.Товары.Сортировать("Номенклатура", Новый СравнениеЗначений);
ЗначениеВРеквизитФормы(ДокументОбъект, "Объект");
КонецПроцедуры
У табличной части объекта метод Сортировать() есть, синтаксис тот же. Номера строк после сортировки пересчитываются сами. Для реквизита формы с таблицей значений схема та же: РеквизитФормыВЗначение("ИмяРеквизита"), сортировка, ЗначениеВРеквизитФормы.
После сортировки табличной части документ становится изменённым. Если пользователь закроет форму без записи, порядок не сохранится — это ожидаемое поведение, а не сбой.
Как сортировать большие таблицы значений без потери скорости
На десятках тысяч строк сам Сортировать() по числам и датам работает быстро. Тормозит обычно то, что вокруг него.
- Сортируйте один раз. Вызов
Сортировать()внутри цикла, после каждой добавленной строки, — главная причина медленного кода. Заполните таблицу целиком, потом упорядочьте. - Сокращайте таблицу до сортировки.
ТЗ.Скопировать(Отбор, "Колонка1, Колонка2")вернёт только нужные строки и колонки. Меньше данных — быстрее и сортировка, и передача на клиент. - Избегайте СравнениеЗначений на больших объёмах. Подготовьте строковую колонку с наименованием и сортируйте по ней.
- Не гоняйте таблицу в запрос ради сортировки. Если данные изначально пришли из запроса, сразу пишите
УПОРЯДОЧИТЬ ПОв нём, а не выгружайте и сортируйте потом. - Не путайте сортировку с поиском. Нужен быстрый поиск — индекс и
НайтиСтроки(). Сортировка ради поиска «глазами» в цикле ничего не ускоряет. - Ключ считайте один раз. Вычисляемую колонку заполняйте одним проходом, а не пересчитывайте в нескольких местах.
Замерить, что именно тормозит, удобно встроенным замером производительности в отладчике Конфигуратора. Часто оказывается, что время уходит не на сортировку, а на обращение к реквизитам ссылок при заполнении ключа.
Ошибки при сортировке и как их исправить
Опечатка в имени колонки. Строка колонок проверяется при выполнении, поэтому ошибка всплывёт только в рантайме. Скопируйте имя из ТЗ.Колонки.Добавить(...), а не набирайте заново. Сверьте и пробелы: между колонками — запятая, между именем и направлением — пробел.
Сортировка результата, а не таблицы. Код ТЗ2 = ТЗ.Сортировать("Сумма") записывает в ТЗ2 значение Неопределено, потому что метод ничего не возвращает. Правильно: сначала ТЗ.Сортировать("Сумма"), потом работать с ТЗ.
Ссылки «вразнобой». Это не ошибка платформы, а сортировка по внутреннему значению ссылки. Добавьте Новый СравнениеЗначений или сортируйте по наименованию.
Числа в строковой колонке. «10» стоит перед «2». Нужен числовой ключ, пример выше.
Разные типы в одной колонке. Колонка без типа, куда попали и числа, и строки, и Неопределено, сортируется не так, как ждёт пользователь. Задайте колонке тип при создании или приведите значения к одному типу перед сортировкой.
Ошибка в запросе на нетипизированной колонке. Колонке нужен тип через ОписаниеТипов. Если таблицу собирают другие модули, создайте новую колонку с типом и перенесите в неё значения.
Порядок «сломался» после изменения строк. Сортировка разовая: добавленные потом строки встают в конец, а после Свернуть() порядок не гарантирован. Пересортируйте таблицу перед выводом.
Частые вопросы о сортировке таблицы значений
Как отсортировать массив в 1С?
СписокЗначений методом ЗагрузитьЗначения(), вызовите СортироватьПоЗначению() и выгрузите обратно через ВыгрузитьЗначения().Можно ли сортировать таблицу значений на клиенте?
ТаблицаЗначений доступен только на сервере и в толстом клиенте. В тонком клиенте сортируйте на сервере в процедуре с &НаСервере, а на форме храните результат в реквизите формы.Есть ли у таблицы значений «обратный» метод, чтобы просто перевернуть порядок?
Убыв. Если ключа нет, добавьте колонку с номером строки и отсортируйте по ней с Убыв.