Разберем одну из классических и весьма коварных проблем, с которой часто сталкиваются разработчики при работе с динамическими списками в 1С:Предприятии. Речь идет о сортировке адресов, содержащих номера домов или квартир с литерами (например, «12а», «11», «2»).
По умолчанию СУБД выполняет стандартную алфавитно-цифровую сортировку строк. В результате мы получаем некорректный с точки зрения человека порядок: «Гагарина 1», «Гагарина 11», «Гагарина 2». Наша задача — настроить систему так, чтобы сортировка происходила по «естественному» (натуральному) принципу: «Гагарина 1», «Гагарина 2», «Гагарина 11».
В этой статье мы подробно рассмотрим несколько способов решения данной задачи: от простых манипуляций в запросе динамического списка до создания архитектурно правильной структуры хранения данных.
Для базы данных любое строковое поле (даже если оно состоит только из цифр) является набором символов. Сравнение строк происходит посимвольно слева направо. Символ «1» меньше символа «2», поэтому строка «11» всегда будет стоять перед строкой «2». Чтобы заставить СУБД сортировать строки как числа, нам необходимо либо привести их к общему виду с одинаковым количеством разрядов (добавив лидирующие нули), либо разделить числовую и текстовую составляющие.
Если изменять структуру метаданных в конфигурации крайне нежелательно, мы можем решить проблему непосредственно на лету в запросе динамического списка. Для этого добавим вычисляемое служебное поле, в котором дополним строковые номера квартир лидирующими нулями.
Проанализируем пример кода, который решает эту задачу для номеров квартир длиной до 3 символов плюс возможная литера (буква):
ВЫБОР
// Если в номере 3 цифры и буква (например, "123а")
КОГДА СправочникЛицевыеСчета.Квартира ПОДОБНО "[^ ][^ ][^ ][абвгдАБВГД]%"
ТОГДА "0" + ПОДСТРОКА(СправочникЛицевыеСчета.Квартира, 1, 3)
// Если в номере 2 цифры и буква (например, "12а")
КОГДА СправочникЛицевыеСчета.Квартира ПОДОБНО "[^ ][^ ][абвгдАБВГД]%"
ТОГДА "00" + ПОДСТРОКА(СправочникЛицевыеСчета.Квартира, 1, 2)
// Если в номере просто 3 цифры (например, "123")
КОГДА СправочникЛицевыеСчета.Квартира ПОДОБНО "[^ ][^ ][^ ]%"
ТОГДА "0" + СправочникЛицевыеСчета.Квартира
// Если в номере 1 цифра и буква (например, "1а")
КОГДА СправочникЛицевыеСчета.Квартира ПОДОБНО "[^ ][абвгдАБВГД]%"
ТОГДА "000" + ПОДСТРОКА(СправочникЛицевыеСчета.Квартира, 1, 1)
// Если в номере просто 2 цифры (например, "12")
КОГДА СправочникЛицевыеСчета.Квартира ПОДОБНО "[^ ][^ ]%"
ТОГДА "00" + СправочникЛицевыеСчета.Квартира
// Во всех остальных случаях (например, однозначное число "1")
ИНАЧЕ "000" + СправочникЛицевыеСчета.Квартира
КОНЕЦ
Важное замечание по синтаксису: Обратите внимание, что в платформе 1С функция ПОДСТРОКА использует нумерацию символов, начинающуюся с 1 (а не с 0, как в некоторых других языках программирования). Поэтому вторым параметром мы передаем единицу.
Благодаря такой конструкции строка «2» превратится в «0002», а строка «11» — в «0011». При сортировке значение «0002» гарантированно окажется выше, чем «0011», что полностью решает поставленную задачу.
Однако у этого метода есть существенный минус — производительность. Использование сложных конструкций ВЫБОР КОГДА совместно с оператором ПОДОБНО заставляет СУБД выполнять сканирование всей таблицы вместо использования индексов. Если в вашем списке будут десятки тысяч записей, динамический список начнет заметно «подтормаживать» при прокрутке и сортировке.
Чтобы избежать падения производительности, перенесем тяжелые вычисления из запроса СУБД на уровень записи объекта. Мы создадим дополнительный строковый реквизит в справочнике — например, КвартираДляСортировки (длиной 10–15 символов) и установим для него свойство Индексировать.
Заполнение этого поля будет происходить автоматически в модуле объекта при его записи. Разберем по шагам, как это реализовать:
КвартираДляСортировки (тип: Строка).ПередЗаписью:
Процедура ПередЗаписью(Отказ)
Если ОбменДанными.Загрузка Тогда
Возврат;
КонецЕсли;
// Формируем стандартизированную строку для сортировки
КвартираДляСортировки = ПолучитьИндексСортировки(Квартира);
КонецПроцедуры
Функция ПолучитьИндексСортировки(Знач ВходнаяСтрока) Экспорт
ЧисловаяЧасть = "";
БуквеннаяЧасть = "";
// Перебираем символы для отделения числа от литеры
Для Поз = 1 По СтрДлина(ВходнаяСтрока) Цикл
Символ = Сред(ВходнаяСтрока, Поз, 1);
Если Найти("0123456789", Символ) > 0 Тогда
ЧисловаяЧасть = ЧисловаяЧасть + Символ;
Иначе
// Все, что идет после первой буквы, считаем буквенной частью
БуквеннаяЧасть = Сред(ВходнаяСтрока, Поз);
Прервать;
КонецЕсли;
КонецЦикла;
Если ЗначениеЗаполнено(ЧисловаяЧасть) Тогда
// Дополняем числовую часть нулями до фиксированной длины (например, 6 символов)
ЧислоФормат = Формат(Число(ЧисловаяЧасть), "ЧЦ=6; ЧДЦ=0; ЧГ=0");
ЧислоСНулями = СтрЗаменить(ЧислоФормат, " ", "0");
Возврат ЧислоСНулями + ВРег(БуквеннаяЧасть);
КонецЕсли;
Возврат ВРег(ВходнаяСтрока);
КонецФункции
Теперь в запросе динамического списка вам достаточно просто выбрать уже готовое проиндексированное поле КвартираДляСортировки. База данных отсортирует записи мгновенно, без каких-либо накладных расходов.
Еще один профессиональный архитектурный подход — раздельное хранение числовой и текстовой частей адреса еще на этапе проектирования базы данных. Вместо одного строкового поля «Адрес» или «Квартира» заведите два реквизита:
НомерКвартирыЧисло (тип: Число) — для хранения непосредственно номера (например, 12).ЛитераКвартиры (тип: Строка) — для букв или дополнительных обозначений (например, «а»).В этом случае в динамическом списке сортировка настраивается сразу по двум полям: сначала по числу (возрастание), а затем по литере (возрастание). Это идеальный вариант с точки зрения чистоты структуры данных.
Какое бы решение вы ни выбрали (вычисление в запросе или готовое индексированное поле), пользователю в форме не обязательно видеть технические нули вроде «00012а». Он должен кликать на привычную колонку «Квартира», но сортировка при этом должна происходить по скрытому полю.
Чтобы реализовать такое переопределение в управляемой форме, выполните следующие настройки:
Квартира.КвартираДляСортировки или имя вычисляемого поля из запроса динамического списка).После этой несложной настройки клик по шапке колонки «Квартира» визуально останется прежним, но платформа автоматически перенаправит команду сортировки СУБД на ваше служебное отформатированное поле. Пользователь получит быструю, правильную и естественную сортировку адресов!