В практике разработчика 1С часто встречается ситуация, когда пользователи (бухгалтеры или менеджеры) вручную редактируют коды в справочниках, удаляя ведущие нули. В справочнике «Контрагенты» или «Номенклатура» вместо стандартного 000000001 появляются записи с кодами 1, 2, 10, 100. Проблема проявляется при попытке построить отчет с сортировкой по коду: система использует лексикографический порядок (строковый), и в результате мы получаем последовательность вида 1, 10, 100, 11, 2, 20. Это происходит потому, что для базы данных символ "1" всегда меньше "2", независимо от того, сколько цифр следует далее.
В данной статье мы подробно разберем несколько способов, как победить эту проблему в запросе и вернуть данным логичный числовой порядок.
Проанализируем ситуацию: почему 10 оказывается выше 2? Потому что при сравнении строк первый символ "1" меньше первого символа "2". Самый простой способ заставить запрос сортировать данные корректно, не прибегая к сложным математическим вычислениям, — это сначала отсортировать данные по длине кода, а уже внутри одной длины — по самому значению кода.
Поскольку в языке запросов 1С (до версии 8.3.20) нет прямой функции СТРДЛИНА, мы можем использовать конструкцию ВЫБОР и функцию ПОДСТРОКА. Разберем этот пример:
ВЫБРАТЬ
Контрагенты.Ссылка КАК Ссылка,
Контрагенты.Код КАК Код,
ВЫБОР
КОГДА ПОДСТРОКА(Контрагенты.Код, 10, 1) <> "" ТОГДА 10
КОГДА ПОДСТРОКА(Контрагенты.Код, 9, 1) <> "" ТОГДА 9
КОГДА ПОДСТРОКА(Контрагенты.Код, 8, 1) <> "" ТОГДА 8
КОГДА ПОДСТРОКА(Контрагенты.Код, 7, 1) <> "" ТОГДА 7
КОГДА ПОДСТРОКА(Контрагенты.Код, 6, 1) <> "" ТОГДА 6
КОГДА ПОДСТРОКА(Контрагенты.Код, 5, 1) <> "" ТОГДА 5
КОГДА ПОДСТРОКА(Контрагенты.Код, 4, 1) <> "" ТОГДА 4
КОГДА ПОДСТРОКА(Контрагенты.Код, 3, 1) <> "" ТОГДА 3
КОГДА ПОДСТРОКА(Контрагенты.Код, 2, 1) <> "" ТОГДА 2
ИНАЧЕ 1
КОНЕЦ КАК ДлинаКода
ИЗ
Справочник.Контрагенты КАК Контрагенты
УПОРЯДОЧИТЬ ПО
ДлинаКода,
Код
Что мы сделали: мы создали виртуальное поле ДлинаКода. Если в десятой позиции кода есть символ, значит длина минимум 10. Если в десятой пусто, а в девятой есть — длина 9. В блоке УПОРЯДОЧИТЬ ПО мы сначала ставим короткие коды, затем длинные. Внутри групп с одинаковой длиной обычная строковая сортировка сработает идеально (10 будет меньше 20, а 100 — меньше 110).
Другой подход заключается в том, чтобы «на лету» привести все коды к одной длине, добавив к ним ведущие нули в виртуальном поле. Рассмотрим вариант реализации через конкатенацию строк. Если мы знаем, что максимальная длина кода в справочнике составляет 9 символов, мы можем применить следующую логику:
ВЫБРАТЬ
Контрагенты.Код КАК ОригинальныйКод,
ВЫБОР
КОГДА Контрагенты.Код ПОДОБНО "_" ТОГДА "00" + Контрагенты.Код
КОГДА Контрагенты.Код ПОДОБНО "__" ТОГДА "0" + Контрагенты.Код
ИНАЧЕ Контрагенты.Код
КОНЕЦ КАК КодДляСортировки
ИЗ
Справочник.Контрагенты КАК Контрагенты
УПОРЯДОЧИТЬ ПО
КодДляСортировки
Однако этот метод довольно громоздкий, если длина кода большая (например, 11 символов в типовых конфигурациях). Выясним причину, почему это неудобно: придется писать 10 веток КОГДА. Поэтому такой способ лучше использовать только для справочников с короткой длиной кода.
Если ваша база работает на современной платформе (версии 8.3.20 и выше), ситуация значительно упрощается. Фирма «1С» расширила язык запросов, добавив в него функции для работы со строками и числами, которые выполняются на стороне СУБД. Теперь мы можем использовать функцию ЧИСЛО или ПРАВО.
Посмотрим на пример использования новых функций:
ВЫБРАТЬ
Контрагенты.Код,
Контрагенты.Ссылка
ИЗ
Справочник.Контрагенты КАК Контрагенты
УПОРЯДОЧИТЬ ПО
СТРДЛИНА(Контрагенты.Код),
Контрагенты.Код
Или, если вы уверены, что в коде только цифры, можно попробовать прямое преобразование (хотя это может замедлить запрос на больших объемах данных):
ВЫБРАТЬ
Контрагенты.Ссылка
ИЗ
Справочник.Контрагенты КАК Контрагенты
УПОРЯДОЧИТЬ ПО
ВЫРАЗИТЬ(Контрагенты.Код КАК ЧИСЛО) // Доступно в последних версиях платформы
Важный нюанс: использование функций преобразования типов в УПОРЯДОЧИТЬ ПО может привести к тому, что СУБД не сможет использовать индексы. Если в справочнике сотни тысяч записей, такой запрос может выполняться медленно.
Если задача стоит в рамках разработки отчета, проанализируем возможности СКД. В СКД можно создать вычисляемое поле, которое не будет участвовать в основном тексте запроса к БД, но будет рассчитано средствами самой платформы перед выводом.
ЧисловойКод с выражением Число(Код).ЧисловойКод.Преимущество этого метода в том, что основной запрос остается «чистым» и быстрым, а преобразованием типов занимается сама 1С при компоновке результата. Это работает даже на старых версиях платформы, так как язык выражений СКД богаче языка запросов.
Разберем ситуацию с точки зрения архитектуры. Если бухгалтеры «перелопатили» коды, это будет создавать проблемы не только в этом конкретном запросе, но и во всех списках, подборах и других отчетах. Программисты рекомендуют не бороться с симптомами, а устранить причину.
Рассмотрим шаги по исправлению данных:
1 превратится в 000000001.ПередЗаписью для справочника Контрагенты. В обработчике проверяйте длину введенного кода и дополняйте его нулями автоматически.Пример кода для нормализации при записи:
Процедура ПриУстановкеНовогоКода(Источник, СтандартнаяОбработка, Префикс)
// Стандартный механизм 1С сам заботится о нулях,
// если пользователь не вмешивается.
КонецПроцедуры
Процедура ПередЗаписью(Источник, Отказ)
Если Источник.ОбменДанными.Загрузка Тогда
Возврат;
КонецЕсли;
// Дополняем код нулями до нужной длины (например, 9)
Пока СтрДлина(Источник.Код) < 9 Цикл
Источник.Код = "0" + Источник.Код;
КонецЦикла;
КонецПроцедуры
Мы рассмотрели несколько путей решения задачи. Если вам нужно быстрое решение в запросе — используйте сортировку по длине через ПОДСТРОКА. Если вы работаете с отчетом — используйте вычисляемые поля СКД. Но самым профессиональным подходом будет нормализация кодов в базе данных: это один раз решит проблему для всех пользователей и всех будущих отчетов, обеспечив высокую производительность системы.