Как в запросе 1С проверить, что один строковый реквизит не содержит значение другого

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

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

Анализ проблемы и стандартного оператора ПОДОБНО

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

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


// ВНИМАНИЕ: Данный код вызовет ошибку синтаксиса
ВЫБРАТЬ
    Справочник1.Ссылка
ИЗ
    Справочник.Справочник1 КАК Справочник1
ГДЕ
    НЕ Справочник1.Реквизит2 ПОДОБНО "%" + Справочник1.Реквизит1 + "%"

Важный момент: В языке запросов 1С запрещена конкатенация (сложение строк) внутри условия ГДЕ для формирования шаблона поиска. Система не может динамически вычислять маску для каждой строки таблицы в процессе выполнения одного запроса к СУБД. Это связано с особенностями трансляции запроса 1С в SQL-запрос.

Решение через оператор ПОДОБНО с использованием параметров

Если нам нужно найти вхождения не динамически (поле в поле), а проверить конкретное значение, мы можем использовать параметры. Разберем, как правильно установить маску поиска в коде 1С перед выполнением запроса. Посмотрим на пример кода:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |   Справочник1.Ссылка КАК Ссылка,
    |   Справочник1.Реквизит1 КАК Номер,
    |   Справочник1.Реквизит2 КАК Описание
    |ИЗ
    |   Справочник.Справочник1 КАК Справочник1
    |ГДЕ
    |   НЕ Справочник1.Реквизит2 ПОДОБНО &МаскаПоиска";

// Формируем маску в коде 1С, добавляя проценты
Запрос.УстановитьПараметр("МаскаПоиска", "%" + ИскомоеЗначение + "%");

РезультатЗапроса = Запрос.Выполнить();

В этом случае % (процент) заменяет любое количество произвольных символов. Однако это решение подходит только тогда, когда мы ищем конкретную строку во всем справочнике. Если же нам нужно для каждой уникальной записи проверить соответствие Реквизит1 внутри Реквизит2, этот метод в чистом виде не сработает.

Оптимальное решение: Использование дополнительного реквизита

Как справедливо отмечают опытные разработчики, лучшим способом решения данной задачи является изменение архитектуры данных. Если бизнес-логика подразумевает, что Реквизит2 всегда должен содержать Реквизит1, стоит добавить вспомогательный реквизит типа Булево (например, РеквизитыСоответствуют).

Разберем по шагам реализацию этого метода:

  1. Добавление реквизита: Добавляем в справочник реквизит ДанныеКорректны (Тип: Булево).
  2. Обработка записи: В модуле объекта справочника в процедуре ПередЗаписью прописываем логику проверки.

Процедура ПередЗаписью(Отказ)
    // Проверяем вхождение одной строки в другую
    Если СтрНайти(Реквизит2, СокрЛП(Реквизит1)) > 0 Тогда
        ДанныеКорректны = Истина;
    Иначе
        ДанныеКорректны = Ложь;
    КонецЕсли;
КонецПроцедуры

Теперь задача поиска элементов, в которых Реквизит2 не содержит Реквизит1, решается мгновенно и максимально эффективно с точки зрения производительности:


ВЫБРАТЬ
    Ссылка
ИЗ
    Справочник.Справочник1
ГДЕ
    ДанныеКорректны = ЛОЖЬ

Преимущества этого метода:
1. Скорость: Поиск по булеву полю работает в тысячи раз быстрее, чем строковый поиск с маской % в начале строки.
2. Индексация: Поле ДанныеКорректны можно проиндексировать, что обеспечит моментальную выборку даже на миллионах записей.
3. Простота запроса: Сам запрос становится элементарным и легко читаемым.

Альтернативный метод: Обработка результата в коде

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


Запрос = Новый Запрос("ВЫБРАТЬ Ссылка, Реквизит1, Реквизит2 ИЗ Справочник.Справочник1");
Выборка = Запрос.Выполнить().Выбрать();

ТаблицаОшибок = Новый ТаблицаЗначений;
ТаблицаОшибок.Колонки.Добавить("Ссылка");

Пока Выборка.Следующий() Цикл
    // Используем функцию СтрНайти для поиска подстроки
    Если СтрНайти(Выборка.Реквизит2, СокрЛП(Выборка.Реквизит1)) = 0 Тогда
        НоваяСтрока = ТаблицаОшибок.Добавить();
        НоваяСтрока.Ссылка = Выборка.Ссылка;
    КонецЕсли;
КонецЦикла;

Этот метод допустим на небольших объемах данных (до нескольких тысяч элементов). Если справочник содержит десятки тысяч записей, такой перебор приведет к заметному ожиданию пользователя и нагрузке на сервер.

Нюансы типов данных (Число vs Строка)

Часто один из реквизитов (например, Реквизит1) имеет тип Число. Помните, что в языке запросов 1С нет функции Строка() или String(). Вы не сможете преобразовать число в строку прямо в запросе для использования в ПОДОБНО. Поэтому предварительная подготовка данных (преобразование в строку при записи или использование программного перебора) становится единственным выходом.

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

Подводя итог, можно сделать следующие выводы:

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

← На главную