Работа с так называемыми "черными запросами" в 1С:Предприятии 7.7 часто преподносит сюрпризы, особенно когда дело касается обработки строковых данных со спецсимволами. Одной из классических проблем является наличие символа двойной кавычки " внутри наименований элементов справочника. Попытка стандартного экранирования кавычек в тексте запроса часто приводит к синтаксическим ошибкам типа "Ожидается ')' перед...". В этой статье мы разберем, почему так происходит, и проанализируем наиболее эффективные способы решения этой задачи.
Проанализируем ситуацию: когда мы динамически формируем текст запроса и пытаемся вставить условие вида Условие(Наименование = "Светильник ""Кольца"""), внутренний парсер 1С 7.7 может некорректно интерпретировать последовательность кавычек. В отличие от встроенного языка программных модулей, парсер запросов имеет свои ограничения. При достижении определенных комбинаций символов он "теряет" закрывающую скобку условия, считая, что строка завершилась раньше времени.
Ситуация осложняется, если список искомых наименований исчисляется сотнями. Конкатенация условий через ИЛИ превращает текст запроса в огромную нечитаемую строку, которая не только работает медленно, но и может упереться в лимит длины строки текста запроса (около 32 768 символов).
Рассмотрим наиболее изящный и производительный способ. Вместо того чтобы вписывать каждое наименование в текст запроса, мы передадим в запрос объект СписокЗначений и будем проверять вхождение текущего наименования в этот список непосредственно в условии.
Разберем этот метод по шагам:
СписокЗначений всеми необходимыми наименованиями (например, прочитанными из внешнего файла DBF).Условие вызовем метод НайтиЗначение() для нашего списка.Посмотрим на пример кода:
// Предположим, СписокНаименований уже заполнен строками из файла
Запрос = СоздатьОбъект("Запрос");
ТекстЗапроса = "
|Без итогов;
|Наим = Справочник.Материалы.Наименование;
|Цена = Справочник.Материалы.Цена;
|Материал = Справочник.Материалы.ТекущийЭлемент;
|Группировка Материал без групп;
|Условие(СписокНаименований.НайтиЗначение(СокрП(Наим)) <> 0);
|";
Если Запрос.Выполнить(ТекстЗапроса) = 0 Тогда
Возврат;
КонецЕсли;
Важный момент: использование СокрП() внутри условия позволяет избежать проблем с лишними пробелами в конце строк, которые часто встречаются в базе данных. Этот метод полностью снимает проблему кавычек, так как сравнение строк происходит внутри метода НайтиЗначение, где объектная модель 1С корректно обрабатывает любые символы.
Если логика сравнения более сложная (например, нужно проверять одновременно и цену, и наименование, или проводить нечеткий поиск), мы можем вынести проверку во внешнюю функцию модуля. Выясним, как это работает: запрос для каждой итерации перебора данных будет обращаться к функции в контексте модуля обработки.
Пример реализации функции и запроса:
Функция ПроверитьСтроку(ТекНаим, ТекЦена)
// Здесь можно реализовать любую сложную логику
// Переменная СЗ_Ключи — это предварительно подготовленный СписокЗначений
КлючПоиска = СокрП(ТекНаим) + "|" + Строка(ТекЦена);
Если СЗ_Ключи.НайтиЗначение(КлючПоиска) > 0 Тогда
Возврат 1; // Условие выполняется
КонецЕсли;
Возврат 0;
КонецФункции
// В тексте запроса
ТекстЗапроса = "
// ... (описание переменных запроса)
|Условие(ПроверитьСтроку(Наим, Цена) = 1);
|";
Рассмотрим подробнее минусы этого подхода: вызов функции из запроса существенно замедляет выполнение, так как происходит постоянное переключение контекста между движком запроса и интерпретатором языка. Однако для небольших и средних справочников это решение является максимально гибким.
Проанализируем ситуацию, когда запрос "не находит" элементы, хотя визуально они идентичны. В 1С 7.7 поиск по строке может быть чувствителен к регистру (в зависимости от настроек базы и используемого драйвера БД). Чтобы поиск работал стабильно, рекомендуется проводить нормализацию данных перед поиском.
Разберем, как правильно подготовить данные:
ВРег().ВРег() для текущего значения из справочника.Пример функции нормализации:
Функция Нормализовать(Стр)
// Удаляем лишние пробелы и приводим к одному регистру
Возврат ВРег(СокрЛП(Стр));
КонецФункции
При добавлении в СписокНаименований используйте СписокНаименований.ДобавитьЗначение(Нормализовать(ЗначениеИзФайла)). Соответственно, условие в запросе примет вид: Условие(СписокНаименований.НайтиЗначение(ВРег(СокрП(Наим))) <> 0);.
Многие программисты пытаются использовать конструкцию Условие(Наим В СписокНаименований). Посмотрим на нюанс, о котором часто забывают: в "семерке" оператор В для строковых данных при работе со списками значений иногда ведет себя как поиск по началу строки (аналог LIKE 'строка%'). Это может привести к тому, что при поиске наименования "Лампа" в выборку попадут и "Лампа 60W", и "Лампа Navigator".
Именно поэтому использование СписокЗначений.НайтиЗначение() внутри условия является более безопасным — оно гарантирует точное совпадение (если не используются дополнительные параметры поиска в методе).
Если ваша задача — поиск по конкретному реквизиту (например, артикулу или цене), и для этого реквизита в конфигураторе установлен признак "Сортировка" или "Отбор по реквизиту", то использование метода ВыбратьЭлементыПоРеквизиту будет работать значительно быстрее черного запроса. Однако для реквизита "Наименование" этот метод недоступен напрямую, так как наименование — это стандартный атрибут с особым индексированием.
В случае с наименованием можно использовать НайтиПоНаименованию(Искомое, 0, 1), но помните, что этот метод позиционируется только на первом найденном элементе. Если в справочнике есть дубли, вам придется использовать цикл с ПолучитьЭлемент() и ручной проверкой соответствия, что при больших объемах данных может быть неэффективно.
Для решения проблемы кавычек в запросах 1С 7.7 наилучшим решением является отказ от динамического формирования текста условия с перечислением строк. Вместо этого:
Условие.ВРег() и СокрП().Соблюдение этих правил позволит вам создавать надежные механизмы импорта и синхронизации данных даже в старых системах на базе 1С 7.7, не опасаясь синтаксических ошибок из-за особенностей парсинга кавычек.