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

Программист 1С v7.7
← На главную

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

Анализ проблемы: почему стандартные методы могут не работать

Представим ситуацию: у нас есть справочник Номенклатура и подчиненный ему справочник НормыРасходов. В справочнике НормыРасходов есть реквизит Элемент (тип Справочник.Номенклатура). Нам нужно найти конкретную запись в НормыРасходов, зная её владельца (конкретную позицию номенклатуры) и значение реквизита Элемент.

Разберем типичные ошибки, которые совершают разработчики при попытке решить эту задачу:

  1. Использование стандартного "черного" запроса. Как мы видим из обсуждения, стандартный объект Запрос может выполняться крайне медленно на больших объемах данных. Это связано с тем, что механизм запросов 1С 7.7 при использовании конструкции Условие часто выполняет полный перебор записей таблицы в базе данных вместо использования индексов.
  2. Связка метода НайтиПоРеквизиту и ИспользоватьВладельца. Это критическая особенность платформы 7.7: метод НайтиПоРеквизиту полностью игнорирует установленный фильтр ИспользоватьВладельца. Система просто ищет первую попавшуюся запись с указанным реквизитом по всей таблице справочника, и эта запись может принадлежать совсем другому владельцу.
  3. Избыточное использование метода ТекущийЭлемент(). Часто программисты пишут конструкции вида Объект.ТекущийЭлемент().Реквизит. Это заставляет систему делать лишние обращения к базе данных, что существенно замедляет выполнение кода, особенно внутри циклов.

Решение через метод ВыбратьЭлементыПоРеквизиту

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

Во-первых, нам необходимо убедиться, что в Конфигураторе для реквизита Элемент справочника НормыРасходов установлен флаг "Сортировка" или "Отбор по реквизиту". Без этого флага метод ВыбратьЭлементыПоРеквизиту будет выполнять сканирование всей таблицы, что нивелирует все преимущества этого способа.

Во-вторых, реализуем код поиска. Мы будем использовать метод ВыбратьЭлементыПоРеквизиту, который работает очень быстро при наличии индекса. Поскольку мы не можем ограничить выборку владельцем на уровне метода, мы проверим владельца вручную внутри цикла Пока.

Посмотрим на пример правильной реализации:


// Создаем объект для работы со справочником
НР2 = СоздатьОбъект("Справочник.НормыРасходов");

// Запускаем выборку по реквизиту "Элемент"
// Третий параметр "0" означает, что нам не важна иерархия
// Четвертый параметр "0" означает, что нам не важна сортировка
НР2.ВыбратьЭлементыПоРеквизиту("Элемент", НужныйЭлемент, 0, 0);

// Перебираем найденные элементы
Пока НР2.ПолучитьЭлемент() = 1 Цикл
    // Проверяем, совпадает ли владелец найденной записи с нашим искомым владельцем
    Если НР2.Владелец = НужныйВладелец Тогда
        Сообщить("Элемент найден: " + НР2.Наименование);
        // Если нам нужен только один элемент, можем прервать цикл
        Прервать;
    КонецЕсли;
КонецЦикла;

В данном примере НужныйЭлемент — это номенклатура, указанная в реквизите, а НужныйВладелец — это основная номенклатура, которой подчинена норма расхода. Этот код работает в десятки раз быстрее, чем стандартный запрос, так как система мгновенно позиционируется на нужных записях по индексу реквизита Элемент.

Оптимизация работы с объектами и типами данных

Разберем подробнее, почему стоит избегать метода ТекущийЭлемент(). В 1С 7.7 объект справочника (переменная, созданная через СоздатьОбъект) сам по себе является указателем на текущую запись. Когда мы вызываем ПолучитьЭлемент(), объект уже содержит все данные этой записи. Обращение НР2.Владелец — это прямое обращение к полю в памяти. Однако вызов НР2.ТекущийЭлемент() заставляет платформу создать полноценную ссылку на объект, что требует дополнительных ресурсов. В контексте поиска это совершенно излишне.

Также проанализируем ситуацию с типами данных. Иногда в справочниках 1С 7.7 реквизиты имеют неопределенный тип (просто "Справочник"). В этом случае поиск может вести себя непредсказуемо. Если вы столкнулись с тем, что ВыбратьЭлементыПоРеквизиту не находит данные, проверьте тип значения, которое вы передаете в качестве образца. Оно должно в точности соответствовать типу, заданному в метаданных.

Альтернативный взгляд: когда стоит использовать прямой SQL

Если ваша база данных работает под управлением Microsoft SQL Server и объем справочника НормыРасходов исчисляется сотнями тысяч или миллионами записей, даже индексированная выборка может быть недостаточно быстрой. В таких случаях профессиональные разработчики 1С 7.7 используют внешние компоненты, такие как 1С++.

Прямой SQL-запрос позволяет выполнить фильтрацию по владельцу и реквизиту одновременно на стороне сервера. Это происходит за миллисекунды, так как SQL-сервер строит оптимальный план выполнения на основе имеющихся индексов (поля PARENID для владельца и SP[ID] для реквизита).

Однако для большинства типовых задач в обычных базах решение через ВыбратьЭлементыПоРеквизиту с последующей проверкой Если НР2.Владелец = ... является золотым стандартом. Оно сочетает в себе простоту написания кода на встроенном языке и высокую скорость работы благодаря использованию штатных индексов платформы.

Итоговые рекомендации

Для успешного решения задачи поиска в подчиненных справочниках 1С 7.7 придерживайтесь следующего плана:

  1. Проверьте индексы: Убедитесь, что реквизит, по которому идет поиск, имеет пометку "Сортировка" в конфигураторе.
  2. Забудьте про НайтиПоРеквизиту для подчиненных справочников: Этот метод не умеет работать с владельцем.
  3. Используйте цикл ВыбратьЭлементыПоРеквизиту: Это самый быстрый способ получить ограниченный набор данных для последующей фильтрации.
  4. Минимизируйте обращения к объектам: Не используйте ТекущийЭлемент() там, где можно обратиться к реквизиту напрямую через переменную справочника.

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

← На главную