Почему запрос в 1С 7.7 не находит строку по фрагменту и как правильно использовать функцию Найти?

Программист 1С v7.7 IT и автоматизация бизнеса
← На главную

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

Анализ проблемы: почему "невидимые" символы ломают поиск

Разберем типичный пример. У нас есть справочник КодТовара, где хранятся артикулы поставщиков. Мы пытаемся найти все записи, в наименовании которых встречается определенная последовательность символов, например, "T131401". Однако запрос возвращает результат только при полном совпадении или не находит ничего, даже если в справочнике есть строка "T1314011".

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

Проанализируем фрагмент кода, который приводит к ошибке:


ВыбФрагмент = "T131401"; // На самом деле здесь могут быть невидимые пробелы из формы
ТекстЗапроса = "
|КакУПоставщика = Справочник.КодТовара.Наименование;
|Условие(Найти(КакУПоставщика, ВыбФрагмент) > 0);
|";

Решение №1: Очистка параметров поиска

Для исправления ситуации необходимо гарантировать, что искомая подстрока не содержит лишних пробелов в начале и в конце. Для этого перед формированием текста запроса следует использовать функцию СокрЛП(). Посмотрим на правильную реализацию:


// Создаем вспомогательную переменную для поиска
ФрагментПоиска = СокрЛП(ВыбФрагмент);

Если ПустаяСтрока(ФрагментПоиска) = 1 Тогда
    Сообщить("Введите текст для поиска!");
    Возврат;
КонецЕсли;

Запрос = СоздатьОбъект("Запрос");
ТекстЗапроса = "//{{ЗАПРОС(Сформировать)
|КакУПоставщика = Справочник.КодТовара.Наименование;
|Группировка КакУПоставщика;
|Условие(Найти(КакУПоставщика, ФрагментПоиска) <> 0);
|"//}}ЗАПРОС
;

Обратите внимание на использование оператора <> 0 вместо > 0. Хотя логически они похожи, в некоторых версиях движка баз данных (особенно в SQL-варианте 7.7) явная проверка на неравенство нулю считается более надежной при работе со встроенными функциями запроса.

Решение №2: Использование преобразования типов

Иногда полезно принудительно привести типы данных внутри самого запроса, используя функцию Строка(). Это помогает избежать проблем, если реквизит справочника имеет неопределенный тип или является ссылкой. Рассмотрим, как выглядит такой "усиленный" запрос:

|Условие(Найти(Строка(КакУПоставщика), Строка(ФрагментПоиска)) > 0);

Однако стоит помнить, что избыточное использование функции Строка() внутри цикла обхода записей запроса может замедлить выполнение, так как платформа будет выполнять преобразование для каждой сканируемой строки таблицы.

Технические нюансы производительности и индексации

Важно понимать, как 1С 7.7 исполняет такие запросы. Когда мы используем Найти() в секции Условие, мы лишаем систему возможности использовать индексы базы данных. Проанализируем последствия:

  1. Полное сканирование (Full Scan): Система вынуждена прочитать каждую запись в таблице справочника, чтобы применить к ней строковую функцию. Если в справочнике 100 000 записей, запрос будет работать долго.
  2. Влияние типа базы данных: В DBF-версии такой поиск выполняется силами самой платформы 1С. В SQL-версии платформа пытается транслировать это условие в SQL-запрос, но возможности стандартного генератора запросов 7.7 ограничены, что часто приводит к неоптимальным планам выполнения.
  3. Регистрозависимость: Помните, что функция Найти() в 7.7 может быть чувствительна к регистру символов. Если вы ищете "артикул", а в базе записано "АРТИКУЛ", поиск может завершиться неудачей. Для решения этой проблемы часто используют перевод обеих частей сравнения в верхний регистр (функция Врег()), но в стандартном языке запросов 7.7 функция Врег() внутри Условие() не поддерживается.

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

Если справочник очень большой и поиск по фрагменту требуется выполнять часто, программисты часто прибегают к использованию внешних компонент, таких как 1C++. Это позволяет писать прямые SQL-запросы, используя оператор LIKE:


// Пример логики прямого запроса через 1С++
ТекстSQL = "
|SELECT id as [Элемент $Справочник.КодТовара]
|FROM $Справочник.КодТовара
|WHERE $Справочник.КодТовара.Наименование LIKE '%" + ФрагментПоиска + "%'
|";

Такой подход работает в десятки раз быстрее стандартного объекта Запрос, так как SQL-сервер гораздо эффективнее справляется с поиском подстрок.

Рекомендации по структуре данных

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

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

← На главную