Как обрезать наименование номенклатуры до запятой и сгруппировать данные в 1С?

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

Часто в практике разработки на 1С возникает задача, когда необходимо обработать строковые данные прямо "на лету". Проанализируем типичную ситуацию: есть таблица, в которой наименование номенклатуры содержит дополнительные свойства через запятую (например, "Стул Оскар, эмаль", "Стул Оскар, морилка"). Нам необходимо получить сводную таблицу, обрезав строку до первой запятой, и просуммировать количество так, чтобы в итоге получить общую строку "Стул Оскар" с общим количеством.

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

Ограничения языка запросов 1С: почему нельзя просто обрезать строку?

Многие разработчики, столкнувшись с подобной задачей, пытаются найти встроенную функцию для работы со строками в языке запросов. Однако в стандартном языке запросов 1С:Предприятие нет встроенной функции для поиска позиции символа (аналога СтрНайти или Найти из встроенного языка).

Нам доступна только функция ПОДСТРОКА(<Строка>, <Начало>, <Длина>). Но чтобы ей воспользоваться, нужно заранее знать точную длину обрезаемой части. Динамически определить, на каком символе находится запятая, исключительно средствами языка запросов нельзя. Теоретически, можно написать сложный ресурсоемкий запрос: сгенерировать числа, посимвольно перебирать каждую ссылку, находить минимальную позицию запятой и заново конкатенировать строку. Но это крайне неэффективный путь, который сильно замедлит работу системы.

Кроме того, выясним еще одну опасность: если использовать функцию ПОДСТРОКА в клиент-серверных базах (например, MS SQL Server), СУБД часто выделяет под результат максимально возможный размер (4000 символов или неограниченную длину). Попытка сгруппировать результат по такому вычисляемому полю может привести к ошибкам нехватки памяти или серьезному замедлению работы (ошибки сортировки в TempDB).

Решение 1: Постобработка результатов запроса в Таблице Значений

Посмотрим на пример решения, который является классическим для платформы 1С. Поскольку запрос не умеет динамически резать строки, мы выполним простую выборку данных, а логику обрезки и группировки реализуем во встроенном языке.

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

Для работы со строками в современных версиях платформы (начиная с 8.3.6) отлично подходит функция СтрРазделить. Разберем по шагам код:


// 1. Выполняем базовый запрос
Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |   Остатки.Номенклатура.Наименование КАК НоменклатураСтрока,
    |   Остатки.Количество КАК Количество
    |ИЗ
    |   РегистрНакопления.ТоварыНаСкладах.Остатки КАК Остатки";

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

// 2. Обрабатываем строки в цикле
Для Каждого СтрокаТЗ Из ТаблицаРезультат Цикл
    // Разбиваем строку по запятой
    МассивПодстрок = СтрРазделить(СтрокаТЗ.НоменклатураСтрока, ",");
    
    // Берем первую часть (до первой запятой) и убираем лишние пробелы
    Если МассивПодстрок.Количество() > 0 Тогда
        СтрокаТЗ.НоменклатураСтрока = СокрЛП(МассивПодстрок[0]);
    КонецЕсли;
КонецЦикла;

// 3. Сворачиваем таблицу: группируем по наименованию, суммируем количество
ТаблицаРезультат.Свернуть("НоменклатураСтрока", "Количество");

Этот подход отлично справляется с задачей, если объем данных не превышает разумных пределов, так как обработка происходит в оперативной памяти сервера.

Решение 2: Использование Системы Компоновки Данных (СКД)

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

Создадим вычисляемое поле, назовем его БазовоеНаименование, и пропишем в нем следующее выражение:


ВЫБОР 
    КОГДА СтрНайти(Наименование, ",") > 0 
    ТОГДА СокрЛП(Лев(Наименование, СтрНайти(Наименование, ",") - 1)) 
    ИНАЧЕ Наименование 
КОНЕЦ

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

Решение 3: Методологическое (правильная архитектура данных)

Проанализируем ситуацию глобально: суть проблемы кроется в изначально неверном ведении нормативно-справочной информации (НСИ). Хранение базового названия товара и его характеристик ("эмаль", "морилка", "МДФ") в виде единой строки в справочнике — это плохая практика.

В типовых конфигурациях 1С (Управление торговлей, ERP, Комплексная автоматизация) для разделения сущностей существует механизм Характеристики номенклатуры.

Если бы учет велся корректно:

При такой архитектуре нам вообще не пришлось бы ничего обрезать. Мы бы просто написали простейший запрос, сгруппировав итоги по ссылке на поле Номенклатура, проигнорировав поле Характеристика. Это обеспечило бы самую высокую производительность и гибкость отчетов.

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

← На главную