Почему метод НайтиПоРеквизиту не находит элемент справочника и как правильно искать данные в 1С?

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

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

Анализ проблемы: опечатки в именах переменных и контекст вызова

Первое, на что необходимо обратить внимание при внезапном получении пустых значений, — соответствие имен переменных. В исходном коде разработчик может непреднамеренно опечататься в похожих идентификаторах, например, обращаться к СтрокиТД.ФинАгент вместо СтрокиТЗ.ФинАгент. В нестрогой модели типизации или при отсутствии проверки метаданных платформой это приводит к обращению к неинициализированному свойству или другой коллекции.

Рассмотрим, как быстро локализовать проблему с помощью базовых инструментов отладки:

  1. Точка останова в отладчике. Установим точку останова непосредственно перед строкой поиска и добавим переменные в табло (Shift+F9). Это позволяет за считанные секунды увидеть актуальное значение и тип структуры.
  2. Проверка фактического типа данных. Вызовем функцию ТипЗнч() для исследуемого поля. Если свойство не существует или не заполнено, тип будет Неопределено:

// Проверим реальный тип значения перед поиском
Сообщить("Тип значения: " + Строка(ТипЗнч(СтрокиТЗ.ФинАгент)));
Сообщить("Фактическое значение: """ + Строка(СтрокиТЗ.ФинАгент) + """");

Особенности работы функции Строка() в платформе 1С

Выясним причину, почему конструкция Строка(Значение) может возвращать пустое значение, даже если переменная кажется заполненной:

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

Подводные камни строковых данных при поиске по реквизиту

Если имена переменных проверены, но метод НайтиПоРеквизиту всё равно не возвращает результат, проанализируем формат самих строковых данных:

  1. Фиксированная длина строки. Если реквизит поиска в метаданных имеет фиксированную длину (например, строка длиной 10), база данных дополняет значение справа пробелами до указанной длины. Строка "456" не равна строке "456 ". Для успешного поиска искомое значение необходимо дополнять пробелами либо изменить тип реквизита на строку переменной длины.
  2. Неразрывные пробелы и спецсимволы. При загрузке данных из внешних файлов (Excel, CSV, веб-сервисы) в строке часто присутствуют неразрывные пробелы (символ с ASCII-кодом 160). Стандартная функция СокрЛП удаляет только обычные пробелы (код 32). В таком случае очистку строки выполняем явно:

ЗначениеОчищенное = СтрЗаменить(СтрокиТЗ.ФинАгент, Символы.НПП, "");
ЗначениеОчищенное = СокрЛП(ЗначениеОчищенное);
  1. Лидирующие нули. Табельные номера и коды элементов часто имеют формат фиксированной разрядности с ведущими нулями (например, "000000456"). Поиск значения "456" в этом случае не найдет объект. Приведем значение к нужному формату перед поиском:

ЧисловойКод = Число(ЗначениеОчищенное);
ИскомыйНомер = Формат(ЧисловойКод, "ЧЦ=9; ЧВН=");

Ограничения метода НайтиПоРеквизиту

Метод НайтиПоРеквизиту удобен для единичных операций, однако имеет существенные архитектурные ограничения, о которых важно помнить:

  1. Отсутствие индексов и падение производительности. Метод формирует прямой SQL-запрос к СУБД. Если для реквизита в конфигураторе не включено свойство Индексировать (или Индексировать с доп. упорядочиванием), СУБД выполняет полное сканирование таблицы (table scan), что критически замедляет работу системы на больших объемах данных.
  2. Поиск только первого совпадения. Метод всегда возвращает исключительно первую найденную ссылку и не позволяет выявить дубликаты.
  3. Неприменимость к сложным типам. Метод не работает со строками неограниченной длины, объектами типа ХранилищеЗначения и реквизитами табличных частей.

Оптимальное решение: пакетный поиск через временные таблицы запроса

Вызов метода НайтиПоРеквизиту внутри цикла по строкам таблицы значений порождает критический антипаттерн — «запрос в цикле». Посмотрим на правильный подход к сопоставлению данных, когда вся таблица передается в один запрос:


// Подготовим таблицу с типизированной колонкой поиска
ТаблицаПоиска = Новый ТаблицаЗначений;
ТаблицаПоиска.Колонки.Добавить("ТабельныйНомер", Новый ОписаниеТипов("Строка",,,, Новый КвалификаторыСтроки(20)));

Для Каждого СтрТЗ Из СтрокиТЗ Цикл
    НоваяСтрока = ТаблицаПоиска.Добавить();
    НоваяСтрока.ТабельныйНомер = СокрЛП(СтрТЗ.ФинАгент);
КонецЦикла;

// Выполним поиск за одно обращение к СУБД
Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	Таб.ТабельныйНомер КАК ТабельныйНомер
    |ПОМЕСТИТЬ ВТ_ИсходныеДанные
    |ИЗ
    |	&ТаблицаПоиска КАК Таб
    |;
    |
    |////////////////////////////////////////////////////////////////////////////////
    |ВЫБРАТЬ
    |	ВТ.ТабельныйНомер КАК ИсходныйНомер,
    |	Сотрудники.Ссылка КАК Сотрудник
    |ИЗ
    |	ВТ_ИсходныеДанные КАК ВТ
    |	ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Сотрудники КАК Сотрудники
    |		ПО ВТ.ТабельныйНомер = Сотрудники.ТабельныйНомер";

Запрос.УстановитьПараметр("ТаблицаПоиска", ТаблицаПоиска);
РезультатЗапроса = Запрос.Выполнить().Выгрузить();

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

← На главную