В процессе разработки отчетов в системе 1С:Предприятие программисты часто сталкиваются с ситуацией, когда данные в итоговой таблице выстраиваются в странном порядке. Особенно это заметно, когда мы работаем со справочниками, где наименования начинаются с цифр, например, СтатьиЗатрат. Часто можно увидеть порядок типа "1", "10", "2", что совершенно не устраивает конечного пользователя. Разберем детально, почему так происходит и как добиться правильного результата.
Прежде всего, проанализируем важный методологический момент, на котором часто спотыкаются начинающие разработчики. В языке запросов 1С существуют два различных оператора: СГРУППИРОВАТЬ ПО и УПОРЯДОЧИТЬ ПО.
Группировка (СГРУППИРОВАТЬ ПО) используется для «схлопывания» одинаковых записей и применения к ним агрегатных функций (сумма, количество, максимум). Хотя платформа 1С часто выводит сгруппированные записи в определенном порядке, это не является гарантированным поведением. Группировка не предназначена для управления порядком строк.
Сортировка (УПОРЯДОЧИТЬ ПО) — это именно тот инструмент, который отвечает за то, в какой последовательности записи будут представлены в результате запроса. Если нам нужно, чтобы «02. Статья» шла раньше, чем «74. Статья», мы должны использовать именно этот оператор.
Выясним причину, по которой числа в строках сортируются «неправильно». В программировании стандартная сортировка строк называется лексикографической. Система сравнивает строки посимвольно слева направо. Рассмотрим пример:
Система сравнивает первый символ: "1" меньше, чем "2". Следовательно, строка "10. Затраты" будет стоять выше, чем "2. Затраты", несмотря на то, что число 10 больше 2. Это фундаментальное свойство строковых данных. Чтобы победить это поведение, нам нужно либо привести строки к единому формату (добавить лидирующие нули), либо извлечь числовую часть для сравнения.
Проанализируем самый простой вариант. Если в запросе вы используете ссылку на объект (например, СтатьяЗатрат), то при группировке или сортировке система может использовать внутренний идентификатор (UUID), что даст хаотичный порядок. Чтобы данные выстроились по алфавиту (с учетом строковой логики), необходимо обращаться к наименованию. Рассмотрим пример кода:
ВЫБРАТЬ
Продажи.СтатьяЗатрат КАК Статья,
СУММА(Продажи.Сумма) КАК Сумма
ИЗ
РегистрНакопления.Продажи КАК Продажи
СГРУППИРОВАТЬ ПО
Продажи.СтатьяЗатрат
УПОРЯДОЧИТЬ ПО
Продажи.СтатьяЗатрат.Наименование
Этот способ решит проблему, если все ваши номера имеют одинаковую длину (например, "01", "02", "10").
Если нам всё же необходимо реализовать подобие «естественной сортировки» (Natural Sort) прямо в запросе, не меняя данные в базе, мы можем прибегнуть к созданию искусственных полей сортировки. Рассмотрим ситуацию, когда нам нужно отсортировать строки по первым двум символам, преобразуя их в логические веса.
Посмотрим на пример реализации с использованием временных таблиц:
ВЫБРАТЬ
Данные.Статья КАК Статья,
// Извлекаем первый символ и присваиваем вес
ВЫБОР
КОГДА ПОДСТРОКА(Данные.Статья.Наименование, 1, 1) = "0" ТОГДА 0
КОГДА ПОДСТРОКА(Данные.Статья.Наименование, 1, 1) = "1" ТОГДА 1
// ... и так далее для всех цифр
ИНАЧЕ 9
КОНЕЦ КАК ВесПервогоСимвола,
// Извлекаем второй символ
ВЫБОР
КОГДА ПОДСТРОКА(Данные.Статья.Наименование, 2, 1) = "1" ТОГДА 1
ИНАЧЕ 0
КОНЕЦ КАК ВесВторогоСимвола
ПОМЕСТИТЬ ВТ_Сортировка
ИЗ
Справочник.СтатьиЗатрат КАК Данные;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ВТ.Статья
ИЗ
ВТ_Сортировка КАК ВТ
УПОРЯДОЧИТЬ ПО
ВТ.ВесПервогоСимвола,
ВТ.ВесВторогоСимвола,
ВТ.Статья.Наименование
Важно: Этот метод громоздкий и значительно замедляет выполнение запроса на больших объемах данных, так как функции ПОДСТРОКА и ВЫБОР мешают оптимизатору СУБД использовать индексы.
Если вы постоянно сталкиваетесь с необходимостью особой сортировки, проанализируем самый эффективный метод. В справочник СтатьиЗатрат следует добавить числовой реквизит Порядок или КодСортировки.
В этом случае задача решается максимально элегантно:
УПОРЯДОЧИТЬ ПО СтатьяЗатрат.Порядок.Это работает мгновенно, так как числа сравниваются математически корректно, и по этому полю можно построить индекс в базе данных.
Если запрос менять нельзя или он слишком сложный, мы можем обработать результат уже после выполнения запроса. Рассмотрим пример с использованием ТаблицыЗначений. Мы можем добавить в таблицу вспомогательную колонку, заполнить её числами, извлеченными из строк, и отсортировать таблицу штатными средствами встроенного языка.
РезультатЗапроса = Запрос.Выполнить();
ТаблицаРезультат = РезультатЗапроса.Выгрузить();
// Добавим колонку для типа Число
ТаблицаРезультат.Колонки.Добавить("ЧисловойПорядок", Новый ОписаниеТипов("Число"));
Для Каждого Строка Из ТаблицаРезультат Цикл
// Простая логика извлечения первых двух цифр из наименования "07. Статья..."
Строка.ЧисловойПорядок = Число(Лев(Строка.СтатьяНаименование, 2));
КонецЦикла;
// Сортируем таблицу по новой колонке
ТаблицаРезультат.Сортировать("ЧисловойПорядок Возр");
Этот метод часто оказывается «спасательным кругом» для сложных отчетов, где логика сортировки слишком запутана для языка SQL, на котором базируются запросы 1С.
Проанализировав ситуацию, можно сделать следующие выводы:
УПОРЯДОЧИТЬ ПО для управления последовательностью.ТаблицеЗначений перед выводом в макет.Выбор конкретного метода зависит от ваших ограничений: можете ли вы менять структуру конфигурации, насколько велик объем данных и насколько критична скорость формирования отчета.