Как в запросе 1С отсортировать строковое поле с цифрами как число?

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

Разберем классическую ситуацию, с которой сталкиваются разработчики 1С при работе с запросами. Допустим, у нас есть строковый реквизит (например, номер или код), в который пользователи заносят только цифры. Проблема заключается в том, что при стандартной сортировке (УПОРЯДОЧИТЬ ПО) платформа выстраивает строки по алфавиту, а не по математическим правилам. В результате мы получаем нелогичный для пользователя порядок: 1, 10, 11, 110, 120, 2, 5. А нам необходимо вывести данные по порядку возрастания числовых значений: 1, 2, 5, 10, 11, 110, 120.

Многие разработчики сначала пытаются решить проблему "в лоб" с помощью конструкции ВЫРАЗИТЬ(Сотрудники.Реквизит1 КАК ЧИСЛО(10,0)). Однако проанализируем этот подход: он не сработает. Согласно синтаксис-помощнику (СП) 1С, функция ВЫРАЗИТЬ предназначена для приведения типов (например, выделения конкретного типа из составного типа данных), а не для динамического преобразования строки в число. Платформа выдаст ошибку о несовместимых типах. Выясним причину: типы данных в базе строго типизированы, и СУБД не умеет налету менять тип поля при простой выборке таким образом.

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

Способ 1: Использование строковых функций в запросе (для платформ 1С 8.3.18 и новее)

Самое изящное и производительное решение появилось в современных релизах платформы. Начиная с версии 8.3.18, в языке запросов стали доступны функции работы со строками, в том числе функция СТРДЛИНА(). Посмотрим на пример реализации.

Чтобы отсортировать строковый реквизит, состоящий только из цифр, как число, нам достаточно использовать многоуровневую сортировку: сначала по длине строки, а затем по самому строковому значению. Разберем по шагам логику:

  1. Сначала запрос выстроит значения по длине: 1 символ ("1", "5"), затем 2 символа ("10", "11"), затем 3 символа ("110", "120").
  2. Внутри группы с одинаковой длиной обычная строковая сортировка по алфавиту работает абсолютно так же, как и числовая.

В тексте запроса это будет выглядеть следующим образом:


ВЫБРАТЬ
    Сотрудники.Наименование КАК Наименование,
    Сотрудники.Реквизит1 КАК Реквизит1
ИЗ
    Справочник.Сотрудники КАК Сотрудники
УПОРЯДОЧИТЬ ПО 
    СТРДЛИНА(Сотрудники.Реквизит1), 
    Сотрудники.Реквизит1

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

Способ 2: Решение на уровне Системы Компоновки Данных (СКД)

Если ваш запрос используется для вывода данных в отчет на базе СКД, то проблему можно решить вообще не трогая сам текст запроса. СКД обладает более широкими возможностями по преобразованию типов благодаря языку выражений.

Рассмотрим подробнее порядок действий:

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

Если в строке могут изредка встречаться неразрывные пробелы или буквы (из-за которых встроенная функция выдаст ошибку), в выражении СКД можно вызвать экспортную функцию из общего модуля, которая предварительно очистит строку от "мусора" и безопасно конвертирует ее. Отчет выведет данные строго по числовому порядку.

Способ 3: Многоэтапная обработка через Таблицу Значений (ТЗ)

Проанализируем ситуацию, когда у нас старая платформа (до 8.3.18) и нет СКД. Классическим рабочим вариантом является постобработка результата запроса. Мы разбиваем задачу на три этапа.

  1. Формируем первоначальный запрос без упорядочивания и выполняем его.
  2. Выгружаем результат запроса в Таблицу Значений. Создаем в ней новую колонку с типом "Число". Обходим всю таблицу в цикле, преобразуя строку в число с помощью встроенного языка 1С, и записываем результат в новую колонку. После этого применяем метод сортировки самой ТЗ по новой колонке.
  3. Если данные нужны для дальнейшей работы в другом запросе, передаем отсортированную Таблицу Значений как параметр во второй запрос.

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


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

Для каждого СтрокаТЗ Из РезультатЗапроса Цикл
    Попытка
        СтрокаТЗ.ЧисловойРеквизит = Число(СокрЛП(СтрокаТЗ.Реквизит1));
    Исключение
        СтрокаТЗ.ЧисловойРеквизит = 0;
    КонецПопытки;
КонецЦикла;

РезультатЗапроса.Сортировать("ЧисловойРеквизит ВОЗР");

Способ 4: Дополнение строки лидирующими нулями

Еще один концептуальный метод решения проблемы строковой сортировки — приведение всех строк к одинаковой длине путем добавления нулей слева (лидирующих нулей). Строки вида "000001", "000110" и "000120" сортируются в алфавитном порядке точно так же, как и числа по возрастанию.

Если вы можете изменить данные в базе один раз программно, это самый быстрый для запросов путь. Мы берем строку, прибавляем к ней много нулей спереди, а затем берем правые символы на заданную длину.

Пример программного приведения длины:


// Приводим к длине 14 символов
НовоеЗначение = Прав("00000000000000" + ИсходнаяСтрока, 14);

Важное примечание: Если изменять данные в базе нельзя (типовая конфигурация), разработчики иногда пытаются реализовать дополнение нулями прямо в тексте запроса. В старых платформах для этого строится громоздкое условие ВЫБОР КОГДА в связке с функцией ПОДСТРОКА(): запрос проверяет, является ли пустой строкой второй символ, третий и так далее, искусственно вычисляя длину и приклеивая нужное количество нулей (например, "000" + Сотрудники.Реквизит1). Код получается объемным и тяжело читаемым, но позволяет выполнить задачу исключительно силами СУБД, без использования ТЗ.

← На главную