Как в запросе 1С определить количество слов в наименовании номенклатуры

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

Приветствуем вас, коллеги! В повседневной практике разработчика 1С часто возникают задачи, связанные с анализом текстовых данных и нормализацией нормативно-справочной информации (НСИ). Одна из таких классических задач — поиск позиций номенклатуры, наименование которых состоит из нескольких слов, или, наоборот, исключение записей, содержащих только одно слово.

В этой статье мы подробно разберем, как решить эту задачу различными методами. Мы проанализируем эволюцию платформы «1С:Предприятие», рассмотрим старые «костыльные» методы, изучим новые возможности современных версий платформы (начиная с 8.3.20) и, наконец, выработаем оптимальное с точки зрения производительности архитектурное решение.

Вариант 1. Простой поиск с использованием оператора ПОДОБНО (базовый подход)

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

Для реализации такой проверки в языке запросов 1С используется оператор ПОДОБНО с шаблоном маски. Рассмотрим простейший пример кода:


ВЫБРАТЬ
    Номенклатура.Ссылка КАК Ссылка,
    Номенклатура.Наименование КАК Наименование
ИЗ
    Справочник.Номенклатура КАК Номенклатура
ГДЕ
    Номенклатура.Наименование ПОДОБНО "% %"

На первый взгляд, это решение выглядит гениально и просто. Символ % в шаблоне означает любую последовательность произвольных символов (включая пустую). Таким образом, шаблон "% %" ищет вхождение хотя бы одного пробела в любой части строки.

Однако при тестировании этого запроса на реальной базе данных мы моментально столкнемся со следующими проблемами:

  1. Пробелы в начале или в конце строки. Если пользователь случайно ввел наименование с ведущим или замыкающим пробелом (например, " Резистор" или "Кабель "), то этот элемент ошибочно попадет в результат запроса, хотя фактически состоит из одного слова.
  2. Множественные пробелы. Наличие лишних пробелов внутри строки нарушит корректность работы более сложных шаблонов.

Чтобы обойти проблему концевых пробелов на старых версиях платформы 1С, разработчики были вынуждены использовать громоздкие конструкции, исключающие пробелы по краям строки:


ГДЕ
    Номенклатура.Наименование ПОДОБНО "% %"
    И НЕ Номенклатура.Наименование ПОДОБНО " %"
    И НЕ Номенклатура.Наименование ПОДОБНО "% "

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

Вариант 2. Современный подход с использованием функций СокрЛП в запросе (платформа 8.3.20+)

Долгое время разработчики 1С жаловались на отсутствие базовых функций работы со строками непосредственно в языке запросов. К счастью, начиная с версии платформы 8.3.20, эта проблема была решена. В язык запросов были добавлены полноценные строковые функции, такие как СокрЛП (TrimAll), СокрЛ (TrimL), СокрП (TrimR) и ДлинаСтроки (StringLength).

Давайте посмотрим, как элегантно теперь решается проблема поиска многословных наименований. Мы можем «обернуть» поле наименования в функцию очистки пробелов прямо внутри запроса:


ВЫБРАТЬ
    Номенклатура.Ссылка КАК Ссылка,
    Номенклатура.Наименование КАК Наименование
ИЗ
    Справочник.Номенклатура КАК Номенклатура
ГДЕ
    СокрЛП(Номенклатура.Наименование) ПОДОБНО "% %"

Такой запрос отрабатывает корректно: функция СокрЛП предварительно удаляет все незначащие пробелы в начале и в конце строки, после чего оператор ПОДОБНО ищет внутренние пробелы-разделители между словами. Это избавляет нас от необходимости писать сложные логические условия.

Вариант 3. Подсчет заданного количества слов (например, более 5 слов)

Теперь усложним задачу. Что если нам нужно вывести позиции, состоящие более чем из 5 слов? На форумах часто предлагают расширить шаблон оператора ПОДОБНО, увеличив количество пробелов в маске:


// Поиск позиций, содержащих 4 или более пробелов (что косвенно указывает на 5 и более слов)
ГДЕ
    СокрЛП(Номенклатура.Наименование) ПОДОБНО "% % % % %"

Вынуждены вас предостеречь: это решение крайне ненадежно! Проанализируем причину. Если пользователь при вводе данных случайно нажал пробел дважды (например, написал "Микроволновая печь Samsung" с двойными пробелами между словами), то система посчитает каждый лишний пробел за отдельный «разделитель» и ошибочно определит, что в строке гораздо больше слов, чем есть на самом деле.

В языке запросов 1С нет функции автозамены подстроки (аналога функции СтрЗаменить или SQL-функции REPLACE). Из-за этого очистить строку от двойных или тройных пробелов непосредственно в запросе стандартными средствами невозможно.

Вариант 4. Точный подсчет слов на встроенном языке (платформа 8.3.6+)

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

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


// Функция возвращает точное количество слов в строке
Функция ОпределитьКоличествоСлов(Знач ВходнаяСтрока) Экспорт
    
    // Удаляем крайние пробелы
    ОчищеннаяСтрока = СокрЛП(ВходнаяСтрока);
    
    // Разделяем строку по символу пробела.
    // Третий параметр Ложь указывает функции ИГНОРИРОВАТЬ пустые подстроки.
    // Это автоматически решает проблему двойных, тройных и любых множественных пробелов!
    МассивСлов = СтрРазделить(ОчищеннаяСтрока, " ", Ложь);
    
    Возврат МассивСлов.Количество();
    
КонецФункции

Этот лаконичный алгоритм работает безупречно. Параметр Ложь (игнорирование пустых строк) гарантирует, что последовательности идущих подряд пробелов не будут интерпретированы как отдельные слова. Функция вернет массив, состоящий только из реальных, заполненных символами слов.

Вариант 5. Оптимальное архитектурное решение (Best Practice)

А теперь давайте поговорим о самом важном аспекте разработки — о производительности системы.

Использование оператора ПОДОБНО с ведущим процентом в шаблоне (ПОДОБНО "% %") — это страшный сон администратора баз данных. На уровне СУБД (будь то MS SQL Server, PostgreSQL или IBM DB2) такой запрос приводит к полному сканированию таблицы (Table Scan или Index Scan). СУБД физически не может использовать индексы, так как поиск выполняется по внутренней части строки. Если в вашем справочнике «Номенклатура» содержится 100 000 или более позиций, этот запрос будет выполняться медленно, нагружая дисковую подсистему и процессор сервера.

Как же поступить правильно? Рекомендуем использовать классический паттерн кэширования рассчитываемых данных.

Шаг 1. Добавление реквизита в справочник.
Добавьте в справочник Номенклатура новый реквизит КоличествоСлов с типом Число (3, 0). Обязательно установите для него свойство Индексировать.

Шаг 2. Расчет значения при записи элемента.
Откройте модуль объекта справочника Номенклатура и пропишите расчет количества слов в обработчике события ПередЗаписью. Это позволит системе вычислять значение один раз при создании или изменении элемента, а не выполнять тяжелые расчеты «на лету» при каждом формировании отчетов или списков.


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

Шаг 3. Использование в запросах.
Теперь ваш запрос для поиска номенклатуры, наименование которой состоит, к примеру, более чем из 5 слов, будет выглядеть невероятно простым, красивым и, что самое главное, он отработает мгновенно благодаря использованию индекса:


ВЫБРАТЬ
    Номенклатура.Ссылка КАК Ссылка,
    Номенклатура.Наименование КАК Наименование
ИЗ
    Справочник.Номенклатура КАК Номенклатура
ГДЕ
    Номенклатура.КоличествоСлов > 5

Резюме

Мы рассмотрели различные варианты решения задачи. Для простых разовых обработок на современных базах (платформа 8.3.20+) отлично подойдет конструкция СокрЛП(Наименование) ПОДОБНО "% %". Однако для высоконагруженных промышленных систем единственным верным и профессиональным решением будет внедрение отдельного индексируемого реквизита с расчетом его значения в модуле объекта при записи через метод СтрРазделить.

← На главную