При разработке в среде «1С:Предприятие» часто возникает задача хранения списков простых данных внутри документов без создания дополнительных табличных частей. Одним из таких способов является использование механизма дополнительных реквизитов (из Библиотеки стандартных подсистем — БСП), где создается реквизит с типом «Строка» и признаком «Многострочный режим». В этот реквизит пользователи могут записывать, например, список артикулов, кодов или серийных номеров — каждый с новой строки.
Однако при попытке выбрать документы по конкретному артикулу из этого списка с помощью обычного запроса разработчики сталкиваются с тем, что стандартное условие равенства = или условие вхождения В не работают. Давайте подробно разберем, почему так происходит, как решить эту задачу с помощью запроса и какие архитектурные нюансы необходимо учитывать.
Когда мы записываем несколько строк в одно текстовое поле, платформа 1С сохраняет их как единую строку, где строки разделены специальными невидимыми символами перевода строки (в платформе это переменная Символы.ПС, что соответствует символу LF с кодом 10 или комбинации CR + LF с кодами 13 и 10).
Если в реквизите записано:
ART-100
ART-200
ART-300
Для СУБД это единая строка вида "ART-100" + Символы.ПС + "ART-200" + Символы.ПС + "ART-300". Поэтому проверка на точное равенство ГДЕ Значение = "ART-200" вернет ложь, так как вся строка целиком не равна искомому артикулу.
---
Для поиска подстроки внутри строки в языке запросов 1С применяется оператор ПОДОБНО. Однако при работе с дополнительными реквизитами БСП есть важный нюанс: реквизит Значение в табличной части ДополнительныеРеквизиты имеет составной тип данных (может хранить ссылки на различные справочники, числа, булево и строки). Оператор ПОДОБНО можно применять исключительно к строковым полям, иначе СУБД вернет ошибку.
Для обхода этого ограничения мы должны явно выразить тип значения как строку ограниченной длины с помощью конструкции ВЫРАЗИТЬ. Рассмотрим пример базового запроса:
ВЫБРАТЬ
Поступление.Ссылка КАК Ссылка,
Поступление.Значение КАК Значение
ИЗ
Документ.ПоступлениеОтПоставщика.ДополнительныеРеквизиты КАК Поступление
ГДЕ
Поступление.Свойство = &СвойствоАртикулы
И ВЫРАЗИТЬ(Поступление.Значение КАК СТРОКА(1000)) ПОДОБНО &ШаблонПоиска
При установке параметров запроса во встроенном языке необходимо окружить искомый артикул символами процента % (так называемыми масками или wildcards), которые обозначают любую последовательность произвольных символов:
Запрос.УстановитьПараметр("СвойствоАртикулы", СсылкаНаСвойствоАртикулы);
// Окружаем артикул процентами для поиска в любой части строки
Запрос.УстановитьПараметр("ШаблонПоиска", "%" + ИскомыйАртикул + "%");
---
Описанный выше простой поиск по маске %Артикул% имеет один существенный недостаток — он может приводить к ложноположительным результатам из-за частичного совпадения строк.
Представим ситуацию: мы ищем артикул "ART-12". В документе записаны артикулы:
ART-123
ART-124
Шаблон "%ART-12%" успешно найдет этот документ, так как строка "ART-123" содержит внутри себя подстроку "ART-12". Но фактически нужного нам товара в документе нет! Чтобы избежать подобных ошибок, нам нужно искать точное совпадение строки с учетом символов перевода строки.
Искомый артикул в многострочном тексте может находиться в одном из четырех положений:
Поскольку платформа 1С не поддерживает конкатенацию строк с символом перевода строки непосредственно в тексте запроса, мы должны подготовить все возможные шаблоны во встроенном языке и передать их в качестве параметров запроса. Разберем этот алгоритм по шагам:
// Получаем символ перевода строки
ПС = Символы.ПС;
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Поступление.Ссылка КАК Ссылка
|ИЗ
| Документ.ПоступлениеОтПоставщика.ДополнительныеРеквизиты КАК Поступление
|ГДЕ
| Поступление.Свойство = &СвойствоАртикулы
| И (ВЫРАЗИТЬ(Поступление.Значение КАК СТРОКА(1000)) = &ТочноеСовпадение
| ИЛИ ВЫРАЗИТЬ(Поступление.Значение КАК СТРОКА(1000)) ПОДОБНО &ВНачале
| ИЛИ ВЫРАЗИТЬ(Поступление.Значение КАК СТРОКА(1000)) ПОДОБНО &ВСередине
| ИЛИ ВЫРАЗИТЬ(Поступление.Значение КАК СТРОКА(1000)) ПОДОБНО &ВКонце)";
Запрос.УстановитьПараметр("СвойствоАртикулы", СсылкаНаСвойствоАртикулы);
// 1. Точное совпадение (если в реквизите всего одна строка)
Запрос.УстановитьПараметр("ТочноеСовпадение", ИскомыйАртикул);
// 2. Артикул в начале текста (после него идет перевод строки и любые символы)
Запрос.УстановитьПараметр("ВНачале", ИскомыйАртикул + ПС + "%");
// 3. Артикул в середине текста (окружен переводом строки с двух сторон)
Запрос.УстановитьПараметр("ВСередине", "%" + ПС + ИскомыйАртикул + ПС + "%");
// 4. Артикул в конце текста (перед ним идет перевод строки)
Запрос.УстановитьПараметр("ВКонце", "%" + ПС + ИскомыйАртикул);
РезультатЗапроса = Запрос.Выполнить();
Такой подход гарантирует стопроцентную точность поиска без риска зацепить похожие по написанию артикулы.
---
Несмотря на то, что написанный выше запрос решает задачу, важно понимать его влияние на производительность системы. Использование оператора ПОДОБНО с ведущим знаком процента (например, %Значение%) заставляет СУБД выполнять полное сканирование таблицы (Table Scan или Index Scan) вместо быстрого поиска по индексу (Index Seek).
Когда документов в базе станет десятки тысяч, этот запрос начнет работать заметно медленнее, вызывая избыточную нагрузку на сервер управления базами данных и провоцируя транзакционные блокировки. Если эта процедура будет выполняться часто (например, при подборе товаров или проведении документов), это может стать критической точкой отказа системы.
---
Если вы проектируете систему на перспективу или имеете возможность изменить конфигурацию, настоятельно рекомендуется отказаться от хранения списков внутри одной многострочной строки. Рассмотрим более надежные и быстрые варианты:
Самое чистое архитектурное решение — добавить к документу собственную табличную часть (например, АртикулыДокумента) через механизм расширений конфигурации. Каждая строка табличной части будет содержать ровно один артикул в индексированном строковом реквизите. В этом случае поиск будет происходить мгновенно через обычное соединение таблиц:
ВЫБРАТЬ РАЗЛИЧНЫЕ
ДокументСсылка.Ссылка КАК Ссылка
ИЗ
Документ.ПоступлениеОтПоставщика КАК ДокументСсылка
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Документ.ПоступлениеОтПоставщика.АртикулыДокумента КАК Артикулы
ПО Артикулы.Ссылка = ДокументСсылка.Ссылка
ГДЕ
Артикулы.Артикул = &ИскомыйАртикул
Если по каким-то причинам не хочется раздувать структуру самого документа, можно создать независимый или подчиненный регистр сведений (например, АртикулыДокументов) с измерениями Документ (тип: документ-ссылка) и Артикул (тип: строка, длина до 50 символов, с включенным индексированием). При проведении или записи документа данные из многострочной строки будут автоматически записываться в этот регистр. Поиск по индексированному измерению регистра сведений в СУБД происходит за миллисекунды.
Если вы работаете в типовой конфигурации и используете БСП, проверьте настройки дополнительных реквизитов. Современные версии БСП поддерживают работу со свойствами, имеющими тип значения «Дополнительное значение» с возможностью выбора множества значений. В этом случае для одного документа система сама создаст несколько строк в табличной части ДополнительныеРеквизиты, и поиск сведется к простому равенству по значению ссылки справочника, что также является крайне быстрой операцией.