Почему в запросе и в Таблице Значений в 1С по-разному сортируются строки с дефисами?

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

При разработке в среде 1С:Предприятие программисты часто сталкиваются с ситуацией, когда данные, упорядоченные в запросе, меняют свой порядок после выгрузки в Таблицу Значений (ТЗ) и повторной сортировки методом Сортировать(). Особенно ярко эта проблема проявляется на строковых кодах справочников, содержащих дефисы, пробелы или другие спецсимволы (например, 00-005 и 000056).

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

В чем суть проблемы? Приведем наглядный пример

Представим, что у нас есть набор текстовых кодов. Если мы упорядочим их в запросе 1С (который транслируется и выполняется на стороне СУБД MS SQL Server), мы получим один результат. Если же мы выгрузим эти данные в Таблицу Значений и применим к ней метод ТЗ.Сортировать("Код Возр"), порядок строк изменится.

Проанализируем тестовый набор данных:

При сортировке в Запросе (на стороне СУБД SQL) результат будет следующим:

  1. 00-005
  2. 000056
  3. 000056094
  4. 00-006
  5. 00-009215

При сортировке в Таблице Значений (на стороне платформы 1С) результат изменится:

  1. 00-005
  2. 00-006
  3. 00-009215
  4. 000056
  5. 000056094

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

Причина 1. Как работает сортировка в СУБД (Word Sort)

Большинство баз данных 1С работают под управлением MS SQL Server или PostgreSQL. Для строковых типов данных (в SQL это тип nvarchar, используемый для хранения текстовых реквизитов 1С) СУБД применяет языковые стандарты сортировки (Collation), например, Cyrillic_General_CI_AS.

Согласно стандартам операционных систем Windows и Unix, при первом проходе сравнения строк используется алгоритм словесной сортировки (Word Sort). В этом режиме такие символы пунктуации, как дефис (-), тире или пробел, практически игнорируются системой.

Они учитываются лишь во вторую очередь, когда буквенно-цифровые части строк абсолютно одинаковы (например, чтобы определить взаимный порядок слов "co-operation" и "cooperation").

Если мы уберем «игнорируемые» дефисы из нашего примера, то СУБД будет сравнивать строки следующим образом:

Сравнивая эти виртуальные значения посимвольно, СУБД выстраивает их в абсолютно логичную цепочку: сначала идет 00005 (оригинал: 00-005), затем 000056, далее 000056094, затем 00006 (оригинал: 00-006) и так далее. В итоге дефисы оказываются разбросанными по всему списку.

Причина 2. Как работает сортировка в ТЗ (String Sort)

В отличие от СУБД, метод платформы ТЗ.Сортировать() работает исключительно в оперативной памяти компьютера на уровне сервера приложений или тонкого клиента 1С. Платформа выполняет посимвольную сортировку (String Sort) на основе числовых кодов символов в таблице Unicode.

Давайте сопоставим коды ключевых символов из нашего примера:

Поскольку сравнение идет строго слева направо посимвольно:

  1. Первые два символа во всех строках одинаковы: "00".
  2. На третьей позиции в строке "00-005" стоит дефис (код 45).
  3. На третьей позиции в строке "000056" стоит ноль (код 48).

Так как 45 меньше 48, платформа однозначно определяет, что строка с дефисом на третьем месте "меньше" строки с цифрой. По этой причине все строки вида 00-... гарантированно поднимаются в самый верх списка ТЗ, опережая любые строки вида 000....

Именно поэтому использование объекта СравнениеЗначений при сортировке Таблицы Значений не решает проблему: этот объект лишь помогает сопоставить типы данных, но базовый посимвольный алгоритм сравнения строк внутри 1С остается неизменным.

Как решить проблему и синхронизировать сортировку?

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

Решение А. Сортируем Таблицу Значений так же, как в Запросе (игнорируя дефисы)

Если вам критически важно сохранить «умную» языковую сортировку СУБД (Word Sort) внутри Таблицы Значений, нам потребуется сымитировать поведение SQL-сервера. Для этого мы временно уберем дефисы из сравнения строк.

Разберем по шагам, как это реализовать программно:

  1. Добавим в Таблицу Значений вспомогательную служебную колонку строкового типа.
  2. В цикле заполним эту колонку значениями оригинального кода, очищенными от дефисов с помощью функции СтрЗаменить().
  3. Выполним сортировку ТЗ по вспомогательной колонке.
  4. Удалим вспомогательную колонку, вернув ТЗ к исходному виду.

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


// Предположим, что ТаблицаДанных уже заполнена результатами запроса
// Добавляем служебную колонку для имитации Word Sort
ИмяСлужебнойКолонки = "КодДляСортировки";
ТаблицаДанных.Колонки.Добавить(ИмяСлужебнойКолонки, Новый ОписаниеТипов("Строка"));

// Заполняем временную колонку очищенными строками
Для Каждого СтрокаТЗ Из ТаблицаДанных Цикл
    // Исключаем дефисы из сравнения, как это делает SQL
    СтрокаТЗ[ИмяСлужебнойКолонки] = СтрЗаменить(СтрокаТЗ.Код, "-", "");
КонецЦикла;

// Сортируем таблицу по подготовленному представлению
ТаблицаДанных.Сортировать(ИмяСлужебнойКолонки + " Возр");

// Удаляем временную колонку перед выводом или дальнейшей обработкой
ТаблицаДанных.Колонки.Удалить(ИмяСлужебнойКолонки);

Этот простой метод позволяет со стопроцентной точностью воспроизвести в оперативной памяти порядок строк, сформированный базой данных.

Решение Б. Сортируем Запрос так же, как в Таблице Значений (посимвольно)

Если вам, наоборот, требуется строгое посимвольное упорядочивание (где спецсимволы и дефисы идут в начале списка по кодам ASCII/Unicode), то полагаться на сортировку внутри SQL-запроса нельзя.

В языке запросов 1С отсутствует поддержка конструкции COLLATE для явного указания бинарного порядка сравнения на уровне СУБД (например, Cyrillic_General_BIN). Поэтому единственным надежным способом синхронизации будет отказ от упорядочивания внутри СУБД.

Разберем по шагам этот алгоритм:

  1. Извлеките данные из базы с помощью запроса, исключив конструкцию УПОРЯДОЧИТЬ ПО из текста запроса. Это также повысит производительность работы СУБД, избавив её от лишней операции.
  2. Выгрузите результат запроса в Таблицу Значений методом Выгрузить().
  3. Выполните сортировку в коде 1С средствами платформы.

Посмотрим на пример реализации:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	Организации.Код КАК Код,
    |	Организации.Ссылка КАК Ссылка
    |ИЗ
    |	Справочник.Организации КАК Организации"; // УПОРЯДОЧИТЬ ПО удалено для унификации

РезультатЗапроса = Запрос.Выполнить();
ТаблицаДанных = РезультатЗапроса.Выгрузить();

// Сортируем строго по символьным кодам в памяти 1С
ТаблицаДанных.Сортировать("Код Возр");

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

← На главную