При разработке отчетов на системе компоновки данных (СКД) часто возникает задача преобразования строковых данных для их последующей сортировки или группировки. Типичный пример — артикулы формата 1.1.1.1. Если сортировать их как обычные строки, порядок будет нарушен (например, «1.10» может оказаться выше, чем «1.2»). Чтобы решить эту проблему, необходимо привести строку к чистому цифровому виду или дополнить сегменты нулями. Рассмотрим подробнее несколько способов реализации этой задачи, начиная от самых простых и заканчивая сложными архитектурными решениями.
Многие разработчики ошибочно полагают, что в выражениях СКД нельзя использовать стандартные функции работы со строками, доступные в языке запросов SQL. Однако язык выражений СКД значительно богаче. Начиная с платформы 8.3.10, функция СтрЗаменить (или StrReplace) официально поддерживается в вычисляемых полях.
Проанализируем шаги для реализации этого метода:
АртикулБезТочек.
СтрЗаменить(Артикул, ".", "")
Если нашей целью является не просто удаление точек, а возможность сортировать это поле как число, мы можем обернуть результат в функцию приведения типов. В СКД это делается так:
Вычислить(СтрЗаменить(Артикул, ".", ""), "Число")
Важный нюанс: Помните, что простое удаление точек может привести к логической ошибке в иерархических артикулах. Например, артикул 1.12.1 превратится в 1121, а артикул 1.1.21 тоже превратится в 1121. Если ваша структура подразумевает уникальность сегментов, этот метод подходит идеально.
Если отчет является внешним и мы не хотим вносить изменения в конфигурацию, или если логика преобразования строки слишком сложна для вычисляемого поля, мы можем воспользоваться программным модулем отчета. Рассмотрим метод подмены данных перед формированием отчета.
В модуле объекта отчета найдем или создадим процедуру ПриКомпоновкеРезультата. Идея заключается в том, чтобы получить данные запросом, программно обработать артикулы с помощью встроенного языка 1С и передать готовую таблицу значений обратно в СКД как внешний набор данных.
Разберем пример кода для такой обработки (чтобы сократить его в несколько раз, можно использовать СКДБилдер):
Процедура ПриКомпоновкеРезультата(ДокументРезультат, ДанныеРасшифровки, СтандартнаяОбработка)
СтандартнаяОбработка = Ложь;
// 1. Получаем данные основным запросом
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ Ссылка, Артикул ИЗ Справочник.Номенклатура";
ТаблицаДанных = Запрос.Выполнить().Выгрузить();
// 2. Добавляем колонку для очищенного артикула
ТаблицаДанных.Колонки.Добавить("АртикулОчищенный", Новый ОписаниеТипов("Строка"));
Для Каждого Строка Из ТаблицаДанных Цикл
Строка.АртикулОчищенный = СтрЗаменить(Строка.Артикул, ".", "");
КонецЦикла;
// 3. Передаем таблицу в СКД
ВнешниеНаборыДанных = Новый Структура;
ВнешниеНаборыДанных.Вставить("ТзАртикулов", ТаблицаДанных);
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, КомпоновщикНастроек.ПолучитьНастройки(), ДанныеРасшифровки);
ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновки.Инициализировать(МакетКомпоновки, ВнешниеНаборыДанных, ДанныеРасшифровки);
ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
ПроцессорВывода.УстановитьДокумент(ДокументРезультат);
ПроцессорВывода.Вывести(ПроцессорКомпоновки);
КонецПроцедуры
В самой схеме СКД в этом случае должен быть создан Набор данных - объект с именем ТзАртикулов и соответствующими полями (см. также примеры программной работы с СКД).
Иногда возникают ситуации, когда использование вычисляемых полей невозможно (например, если требуется фильтрация по этому полю на уровне базы данных для оптимизации скорости). В старых версиях платформы или при жестких требованиях к SQL-запросу можно применить метод «разбора» строки.
Рассмотрим ситуацию, когда количество точек фиксировано. В этом случае мы можем использовать конструкцию ПОДСТРОКА. Если же количество точек произвольное, применяется более сложная техника с использованием вспомогательной таблицы чисел. Посмотрим на пример запроса, который «выбрасывает» все символы, кроме цифр, и склеивает их обратно:
// Создаем временную таблицу чисел для итерации по строке
ВЫБРАТЬ 1 КАК П ПОМЕСТИТЬ Р16 ОБЪЕДИНИТЬ ВЫБРАТЬ 2 ... (и так далее до 16-32)
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
Дано.Артикул КАК ИсходныйАртикул,
СУММА(ПОДСТРОКА(Дано.Артикул, Числа.П, 1)) КАК ТолькоЦифры
ИЗ
Справочник.Номенклатура КАК Дано
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Р16 КАК Числа
ПО Числа.П <= ДЛИНА(Дано.Артикул)
ГДЕ
ПОДСТРОКА(Дано.Артикул, Числа.П, 1) В ("0", "1", "2", "3", "4", "5", "6", "7", "8", "9")
СГРУППИРОВАТЬ ПО
Дано.Артикул
Этот метод достаточно тяжел для базы данных, так как создает декартово произведение строк на таблицу чисел, но он позволяет решить задачу исключительно средствами SQL без привлечения кода 1С.
Выясним причину, по которой обычное удаление точек может не подойти. Если у вас есть артикулы 1.2 и 1.10, то после удаления точек они станут 12 и 110. Сортировка будет верной. Но если артикулы имеют вид 1.1.2 и 1.1.10, они превратятся в 112 и 1110.
Для максимально корректной сортировки в СКД рекомендуется использовать дополнение нулями каждого сегмента. Проанализируем пример: вместо преобразования в 1111, мы преобразуем 1.1.1.1 в 00001.00001.00001.00001.
В вычисляемом поле это можно реализовать через громоздкую, но эффективную конструкцию ВЫБОР, проверяющую наличие точек и дополняющую каждый найденный фрагмент лидирующими нулями до 5 или 10 знаков. Такое поле в СКД можно использовать как «Поле для упорядочивания» для основного артикула, тогда пользователь будет видеть привычный формат 1.1.1.1, но при нажатии на кнопку сортировки данные выстроятся в строгом логическом порядке.