Как найти номенклатуру по полному наименованию (строка неограниченной длины) в 1С?

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

При разработке интеграций, загрузок прайс-листов или обменов данными мы часто сталкиваемся с задачей поиска элементов справочника по наименованию. В конфигурациях 1С (например, «Управление торговлей 10.3», «УПП» или «Комплексная автоматизация») у справочника Номенклатура часто бывает два реквизита: Наименование (краткое, строка ограниченной длины) и НаименованиеПолное (строка неограниченной длины).

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

Почему возникает ошибка «Неверно указан реквизит»?

Рассмотрим типичную ситуацию. У нас есть таблица значений (например, из Excel), где указано полное название товара. Мы пытаемся найти товар следующим кодом:


// ОШИБОЧНЫЙ КОД
Номенклатура = Справочники.Номенклатура.НайтиПоРеквизиту("НаименованиеПолное", ИскомаяСтрока);

В результате платформа выдает ошибку: «Ошибка при вызове метода контекста (НайтиПоРеквизиту): Неверно указан реквизит».

Причина кроется в устройстве базы данных. Метод НайтиПоРеквизиту работает быстро, потому что использует индексы СУБД. Реквизит НаименованиеПолное имеет тип Строка неограниченной длины. В терминах SQL это типы данных (text, varchar(max) и подобные), которые не подлежат обычному индексированию из-за своего размера. Платформа 1С заранее знает об этом ограничении и блокирует попытку поиска по такому полю еще на этапе исполнения кода.

Разберем работающие способы обхода этого ограничения.

Способ 1. Поиск запросом с приведением типа (ВЫРАЗИТЬ)

Самый доступный способ, не требующий изменения конфигурации — использование языка запросов. Поскольку напрямую сравнивать строки неограниченной длины в условии ГДЕ не всегда оптимально или возможно (в зависимости от СУБД), мы должны использовать оператор ВЫРАЗИТЬ (CAST), чтобы привести строку к ограниченной длине.

Давайте напишем функцию поиска:


Функция НайтиПоПолномуНаименованию(ИскомоеНаименование)
	
	Запрос = Новый Запрос;
	Запрос.Текст = 
		"ВЫБРАТЬ ПЕРВЫЕ 1
		|	Номенклатура.Ссылка КАК Ссылка
		|ИЗ
		|	Справочник.Номенклатура КАК Номенклатура
		|ГДЕ
		|	ВЫРАЗИТЬ(Номенклатура.НаименованиеПолное КАК СТРОКА(1000)) = &НаименованиеПолное";
	
	// Обрезаем искомую строку до 1000 символов, чтобы совпадало с условием запроса
	СтрокаПоиска = Лев(СокрЛП(ИскомоеНаименование), 1000);
	
	Запрос.УстановитьПараметр("НаименованиеПолное", СтрокаПоиска);
	
	Результат = Запрос.Выполнить();
	Если Не Результат.Пустой() Тогда
		Возврат Результат.Выгрузить()[0].Ссылка;
	Иначе
		Возврат Справочники.Номенклатура.ПустаяСсылка();
	КонецЕсли;
	
КонецФункции

В этом примере мы приводим НаименованиеПолное к строке длиной 1000 символов. Этого обычно достаточно для уникальной идентификации товара. Обратите внимание: использование функций над полями в секции ГДЕ может привести к полному сканированию таблицы (Full Table Scan), что на больших базах может работать медленно.

Способ 2. Массовый поиск через временные таблицы

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

Алгоритм действий:

  1. Помещаем наши данные (названия из файла) во временную таблицу.
  2. Соединяем временную таблицу со справочником Номенклатура.
  3. В условии соединения используем ВЫРАЗИТЬ.

Запрос = Новый Запрос;
Запрос.УстановитьПараметр("ТЗ", ТЗ_ИзФайла); // В ТЗ должна быть колонка НаименованиеПолное

Запрос.Текст = 
	"ВЫБРАТЬ
	|	ВходящиеДанные.НомерСтроки КАК НомерСтроки,
	|	ВходящиеДанные.НаименованиеПолное КАК НаименованиеПолное
	|ПОМЕСТИТЬ ВТ_Данные
	|ИЗ
	|	&ТЗ КАК ВходящиеДанные
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|ВЫБРАТЬ
	|	ВТ_Данные.НомерСтроки,
	|	ВТ_Данные.НаименованиеПолное,
	|	ЕСТЬNULL(СпрНоменклатура.Ссылка, ЗНАЧЕНИЕ(Справочник.Номенклатура.ПустаяСсылка)) КАК НоменклатураСсылка
	|ИЗ
	|	ВТ_Данные КАК ВТ_Данные
	|		ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК СпрНоменклатура
	|		ПО (ВЫРАЗИТЬ(ВТ_Данные.НаименованиеПолное КАК СТРОКА(1000)) = ВЫРАЗИТЬ(СпрНоменклатура.НаименованиеПолное КАК СТРОКА(1000)))";

Выборка = Запрос.Выполнить().Выбрать();

Важное предупреждение: Данный метод также ресурсоемкий. SQL-серверу придется преобразовать поле НаименованиеПолное для каждой записи в справочнике номенклатуры, чтобы выполнить соединение. Если в базе 100 000 товаров, запрос может выполняться ощутимое время.

Способ 3. Изменение метаданных (Ограничение длины)

Если у вас есть возможность вносить изменения в конфигурацию, рассмотрите вариант изменения типа реквизита НаименованиеПолное.

Вместо Строка неограниченной длины установите Строка (1024) (или другую разумную длину). После этого:

  1. В свойствах реквизита станет доступен флажок Индексировать.
  2. Если установить индексирование, метод НайтиПоРеквизиту начнет работать штатно и очень быстро.

Однако, будьте осторожны: если в базе уже есть товары с описанием длиннее 1024 символов, при реструктуризации эти данные будут обрезаны.

Способ 4. Использование хеширования (Best Practice)

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

Как это реализовать:

  1. Добавляем в справочник Номенклатура реквизит ХешНаименования (Строка, 32 символа, Индексировать).
  2. В модуле объекта в процедуре ПередЗаписью вычисляем MD5-хеш от полного наименования.

// Пример кода в модуле объекта Справочника Номенклатура
Процедура ПередЗаписью(Отказ)
    ХешФункция = Новый ХешированиеДанных(ХешФункция.MD5);
    // Нормализуем строку перед хешированием (убираем лишние пробелы и приводим к нижнему регистру)
    ТекстДляХеша = НРег(СокрЛП(НаименованиеПолное));
    ХешФункция.Добавить(ТекстДляХеша);
    
    // Записываем результат в новый реквизит
    ЭтотОбъект.ХешНаименования = Строка(ХешФункция.ХешСумма);
EndProcedure

Теперь поиск становится молниеносным, так как мы ищем по короткой индексированной строке:


// Вычисляем хеш искомой строки
ХешФункция = Новый ХешированиеДанных(ХешФункция.MD5);
ХешФункция.Добавить(НРег(СокрЛП(ИскомаяСтрока)));
ИскомыйХеш = Строка(ХешФункция.ХешСумма);

// Мгновенный поиск
НайденнаяСсылка = Справочники.Номенклатура.НайтиПоРеквизиту("ХешНаименования", ИскомыйХеш);

Нюансы при работе со строками

При поиске, особенно если данные приходят из внешних источников (Excel, XML), часто возникают проблемы с невидимыми символами. Визуально строки одинаковы, но программно они не равны. При регулярном импорте торговых документов сопоставление объектов автоматизирует обработка загрузки отчётов о продажах в БП 3 из Excel, которая идентифицирует номенклатуру и загружает её в бухгалтерский учёт.

Рекомендуем перед поиском (или вычислением хеша) очищать строку:

  1. Использовать СокрЛП() для удаления пробелов по краям.
  2. Удалять неразрывные пробелы (код символа 160), которые часто попадают из Excel или веб-страниц.

Функция НормализоватьСтроку(ВходнаяСтрока)
    // Заменяем неразрывный пробел на обычный
    СтрокаБезНП = СтрЗаменить(ВходнаяСтрока, Символ(160), " ");
    // Удаляем лишние пробелы и переводы строк
    Возврат СокрЛП(СтрокаБезНП);
КонецФункции

Применяйте эти методы в зависимости от ваших прав доступа к конфигурации и требований к производительности системы.

← На главную