Разработка и Конфигурация · 12 мин
1С: что быстрее — НайтиПоРеквизиту или запрос
Сравниваем НайтиПоРеквизиту и запрос в 1С: когда метод быстрее, роль индексов, типовые ошибки, замеры производительности и практические рекомендации для разработчика.
Вопрос о том, что быстрее — метод НайтиПоРеквизиту() или запрос с условием ГДЕ, — один из самых частых у разработчиков 1С, столкнувшихся с медленным поиском элементов справочника. Короткий ответ: при наличии индекса по реквизиту оба способа работают сопоставимо, потому что НайтиПоРеквизиту внутри платформы фактически выполняет тот же запрос к базе данных. Разница появляется из-за индексов, состава выбираемых полей и объёма данных.
Метод НайтиПоРеквизиту() применяется к менеджеру справочника, плана счетов, плана видов характеристик или плана обмена и возвращает ссылку на объект с заданным значением реквизита. Запрос с конструкцией ВЫБРАТЬ Ссылка ГДЕ Реквизит = &Значение решает ту же задачу, но даёт больше контроля: можно выбрать несколько полей, соединить таблицы, отфильтровать помеченные на удаление и обработать сразу пачку значений.
На практике выбор определяется не синтаксисом, а тремя факторами: есть ли индекс по полю поиска, сколько значений нужно найти за одну операцию и нужна ли только ссылка или ещё данные объекта. Ниже разберём каждый фактор с конкретными признаками и способами проверки.
Как устроен поиск внутри платформы
Когда вызывается Справочники.Номенклатура.НайтиПоРеквизиту("Артикул", Значение), платформа генерирует обращение к СУБД, по смыслу эквивалентное выборке ссылки по условию на реквизит. Никакой «магической» оптимизации в самом методе нет — скорость определяется планом, который построит СУБД, и наличием подходящего индекса.
То же самое происходит при выполнении запроса ВЫБРАТЬ Ссылка ИЗ Справочник.Номенклатура ГДЕ Артикул = &Артикул. Если по колонке артикула есть индекс, оба варианта уходят в index seek и возвращают результат за миллисекунды даже на таблицах с миллионами записей. Если индекса нет, оба варианта выполняют полное сканирование таблицы — и одинаково медленно.
Отсюда главный практический вывод: спор «метод против запроса» вторичен. Первичен вопрос — индексирован ли реквизит, по которому идёт поиск. Без индекса ускорить выборку сменой синтаксиса не получится.
Когда НайтиПоРеквизиту оправдан
Метод удобен для одиночного поиска, когда нужна только ссылка и реквизит гарантированно индексирован. Код получается короче и читается проще: одна строка вместо описания запроса, установки параметров и обхода выборки. В типовых конфигурациях так обычно ищут контрагента по ИНН, номенклатуру по артикулу, валюту по коду.
Дополнительный плюс — метод возвращает пустую ссылку, если ничего не найдено, что легко проверить через Если НайденнаяСсылка.Пустая() Тогда. Не нужно проверять пустоту результата запроса и вытаскивать строку выборки.
Ограничения метода тоже существенны. Он ищет только по одному реквизиту за вызов, не позволяет выбрать дополнительные поля и не умеет искать пачкой. Если в цикле обрабатывается тысяча значений, тысяча вызовов НайтиПоРеквизиту породит тысячу отдельных обращений к серверу СУБД — и это уже реальная проблема производительности.
Когда запрос быстрее и правильнее
Запрос выигрывает в трёх типовых сценариях. Первый — пакетный поиск: вместо цикла с одиночными вызовами передаётся таблица значений или массив, и СУБД одним обращением возвращает все найденные ссылки. Выигрыш на больших объёмах достигает десятков и сотен раз за счёт устранения сетевых и серверных накладных расходов на каждый вызов.
Второй сценарий — поиск с дополнительными условиями: например, найти элемент по артикулу, но только не помеченный на удаление и принадлежащий конкретной папке. НайтиПоРеквизиту такого не умеет, пришлось бы получать объект и проверять поля в коде, что означает чтение лишних данных.
Третий сценарий — когда кроме ссылки нужны поля элемента. Запрос вида ВЫБРАТЬ Ссылка, Наименование, СтавкаНДС вернёт всё одним обращением. Через НайтиПоРеквизиту вы получите ссылку, а затем при обращении к реквизитам объекта платформа выполнит ещё одно чтение из базы — итого два обращения вместо одного.
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Товары.Артикул КАК Артикул,
| Спр.Ссылка КАК Ссылка
|ПОМЕСТИТЬ ВТ_Товары
|ИЗ &ТаблицаТоваров КАК Товары
|;
|ВЫБРАТЬ
| Т.Артикул КАК Артикул,
| Спр.Ссылка КАК Ссылка
|ИЗ ВТ_Товары КАК Т
| ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК Спр
| ПО Спр.Артикул = Т.Артикул
|ГДЕ НЕ Спр.ПометкаУдаления";
Запрос.УстановитьПараметр("ТаблицаТоваров", ТаблицаТоваров);
Результат = Запрос.Выполнить().Выгрузить();Роль индексов: главный фактор скорости
По умолчанию платформа индексирует код, наименование (при определённых настройках длины), ссылку и служебные поля. Произвольный реквизит справочника по умолчанию не индексируется. Если поиск идёт по неиндексированному реквизиту на таблице в сотни тысяч записей, и метод, и запрос будут сканировать всю таблицу — время вырастает линейно с ростом данных.
Проверить и включить индексирование можно в конфигураторе: откройте свойства реквизита и установите флаг «Индексировать» (или «Индексировать с доп. упорядочиванием», если нужна сортировка). После изменения потребуется обновление конфигурации базы данных — платформа построит индекс, на больших таблицах это может занять заметное время и потребовать монопольного доступа.
Важно понимать цену индекса: каждый индекс замедляет запись и занимает место, потому что СУБД поддерживает его актуальность при каждой вставке и изменении. Поэтому индексировать стоит только реквизиты, по которым реально выполняется поиск или соединение, а не все подряд.
Как выбрать подход
Как замерить и проверить на практике
Прежде чем оптимизировать, убедитесь, что проблема именно в поиске. Простейший способ — замер через ТекущаяУниверсальнаяДатаВМиллисекундах() до и после фрагмента кода либо замер производительности в конфигураторе (Отладка → Замер производительности). Замер покажет, какая строка кода занимает время и сколько раз она вызывается.
Для глубокой диагностики на клиент-серверной базе полезен технологический журнал с фильтром по событиям DBMSSQL/DBPOSTGRS и длительности: вы увидите реальный текст обращения к СУБД и его время. Если один и тот же запрос поиска повторяется тысячи раз — это подтверждение проблемы «поиска в цикле».
Дополнительно проверьте план запроса средствами СУБД (план выполнения в SQL Server Management Studio или EXPLAIN в PostgreSQL). Признак нормы — операция Index Seek по индексу реквизита. Признак проблемы — Clustered Index Scan или Table Scan по основной таблице справочника при условии по реквизиту.
Как проверить результат оптимизации
Index Seek по индексу реквизита поиска
Scan по всей таблице справочника
Одно обращение на пакет значений
Тысячи одинаковых одиночных запросов
Линейно растёт от объёма, без резких провалов
Обработка «висит» минутами на больших пачках
Время поиска почти не меняется
Время растёт пропорционально числу элементов
Типовые ошибки и ограничения
Частая ошибка — поиск по реквизиту составного типа или по строке неограниченной длины. Такие поля не индексируются, и поиск по ним всегда будет сканированием, независимо от выбранного синтаксиса. Если бизнес-логика требует поиска по такому полю, правильное решение — добавить отдельный индексируемый реквизит с нормализованным значением и искать по нему.
Вторая ошибка — сравнение с приведением типов в запросе, например условие по строковому представлению числа или использование функций над полем в условии (ПОДСТРОКА, ВЫРАЗИТЬ в ГДЕ). Такие конструкции мешают СУБД использовать индекс, и поиск снова превращается в сканирование.
Третья ошибка — игнорирование пометки удаления. НайтиПоРеквизиту вернёт и помеченный на удаление элемент, если он подходит под условие. Если логика этого не предполагает, нужен запрос с явным условием НЕ ПометкаУдаления, иначе в обменах и загрузках появятся «двойники» и ссылки на удаляемые объекты.
Итоговые рекомендации
Для одиночного поиска ссылки по индексированному реквизиту выбирайте НайтиПоРеквизиту — это короче и не медленнее запроса. Для пакетного поиска, составных условий и выборки полей — всегда запрос. Разница в скорости между методами почти всегда определяется не синтаксисом, а количеством обращений к базе и наличием индекса.
Перед любой оптимизацией сделайте замер и посмотрите план запроса на СУБД. Если видите сканирование таблицы — добавьте индекс по реквизиту поиска. Если видите тысячи одинаковых обращений — убирайте поиск из цикла в один запрос с временной таблицей. Эти два приёма закрывают подавляющее большинство проблем со скоростью поиска элементов в 1С.
Что важно запомнить
НайтиПоРеквизиту и запрос при наличии индекса работают одинаково быстро — платформа в обоих случаях идёт по индексу. Реальный выигрыш даёт не выбор синтаксиса, а индекс по реквизиту поиска и замена цикла одиночных вызовов одним пакетным запросом.
Частые вопросы
НайтиПоРеквизиту находит помеченные на удаление элементы?
Да, метод не фильтрует по пометке удаления и вернёт такой элемент, если значение реквизита совпало. Если помеченные элементы не должны участвовать в поиске, используйте запрос с условием ГДЕ НЕ ПометкаУдаления или проверяйте пометку после получения ссылки.
Чем НайтиПоРеквизиту отличается от НайтиПоКоду и НайтиПоНаименованию?
Принципиально — ничем: все три метода выполняют поиск по индексированному полю. Разница в том, что код и наименование индексируются платформой по умолчанию, а произвольный реквизит — только если включено свойство «Индексировать». По скорости на индексированных полях они эквивалентны.
Как узнать, индексируется ли реквизит, не открывая конфигуратор?
Косвенный признак — замер: поиск по индексированному полю почти не зависит от размера справочника, по неиндексированному — замедляется пропорционально росту данных. Точный ответ даёт план запроса на СУБД: Index Seek означает наличие индекса, Scan — его отсутствие. Также список индексов таблицы виден средствами самой СУБД.
Почему запрос с условием по реквизиту всё равно медленный, хотя индекс есть?
Частые причины: в условии применяется функция к полю (подстрока, приведение типа), тип параметра не совпадает с типом поля и СУБД выполняет неявное преобразование, либо статистика устарела и оптимизатор выбрал неудачный план. Проверьте план запроса и уберите функции из условия, приведите параметр к точному типу поля.
Что использовать для поиска в табличной части документа?
Метод Найти() и НайтиСтроки() табличной части работают с данными уже в памяти и не обращаются к СУБД — для объекта, который уже прочитан, это правильный и быстрый путь. Запрос к табличной части нужен только когда поиск идёт по базе без чтения самого документа, например «найти все документы, где в товарах есть позиция».
Есть ли смысл заменять все НайтиПоРеквизиту на запросы «для скорости»?
Нет. Одиночные вызовы по индексированным реквизитам работают одинаково быстро в обоих вариантах, а массовая замена только увеличит объём кода. Меняйте на запрос там, где замер показал проблему: поиск в цикле, нужны дополнительные поля или составные условия отбора.