При решении задач сопоставления данных разработчики нередко сталкиваются с необходимостью объединить таблицы не по точному равенству ссылок или чисел, а по вхождению одной строки в другую. Например, когда требуется найти документы по фрагменту номера или даты, указанному пользователем в текстовом реквизите Комментарий.
Рассмотрим, можно ли использовать конструкцию ПОДОБНО в секции ПО при соединении таблиц, с какими ограничениями платформы 1С при этом можно столкнуться и как правильно решить задачу сопоставления данных.
Язык запросов 1С:Предприятия полностью поддерживает оператор ПОДОБНО внутри секции ПО при выполнении левого, правого или полного соединения. В условии можно выполнять конкатенацию строковых выражений с помощью оператора + и спецсимволов шаблона % или _.
Посмотрим на рабочий пример простого соединения по вхождению подстроки:
ВЫБРАТЬ
"конверс" КАК Поле1
ПОМЕСТИТЬ ВТ_Соединение
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ПлатежноеПоручение.НазначениеПлатежа,
ВТ_Соединение.Поле1
ИЗ
Документ.ПлатежноеПоручениеВходящее КАК ПлатежноеПоручение
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_Соединение КАК ВТ_Соединение
ПО ПлатежноеПоручение.НазначениеПлатежа ПОДОБНО "%" + ВТ_Соединение.Поле1 + "%"
Несмотря на то что синтаксически платформа допускает такие конструкции, на практике попытка соединить произвольные таблицы часто завершается ошибкой «Неверные параметры» или ошибками трансляции запроса в СУБД. Разберем ключевые причины этих ошибок.
Псевдофункции ПРЕДСТАВЛЕНИЕ() и ПРЕДСТАВЛЕНИЕССЫЛКИ() обрабатываются платформой на клиенте или сервере приложений уже после выполнения основного SQL-запроса к базе данных. По этой причине их категорически нельзя использовать в секциях ГДЕ, ПО, ИМЕЮЩИЕ или внутри строковых выражений и конкатенаций.
В языке запросов 1С функция ВЫРАЗИТЬ() предназначена только для уточнения составных ссылочных типов и ограничения максимальной длины строк. Преобразовать значение типа Дата или Число в Строку средствами чистого запроса невозможно. Функции извлечения частей даты (ГОД(), МЕСЯЦ(), ДЕНЬ()) возвращают числовые значения, которые также нельзя сложить со строкой через +.
Реквизит Комментарий практически во всех типовых объектах имеет тип Строка неограниченной длины. В запросах 1С строки неограниченной длины запрещено напрямую использовать в условиях соединения и операциях конкатенации. Их обязательно нужно приводить к фиксированной длине с помощью функции ПОДСТРОКА(), например: ПОДСТРОКА(Документ.Комментарий, 1, 1000).
Условие вида ПОДОБНО "%" + Поле + "%" полностью исключает использование индексов таблиц СУБД. В результате соединение выполняется методом полного сканирования и декартова перебора (Nested Loop), что на реальных базах данных приводит к многоминутным зависаниям и блокировкам.
Проанализируем практические способы реализации поиска и сопоставления документов, когда исходные данные содержат даты и номера в строковом комментарии.
Если сопоставление выполняется в рамках внешнего отчета или обработки, наиболее надежный подход — сформировать нужные строковые представления даты и номера на встроенном языке 1С, передать их в запрос в виде таблицы значений и соединить с ограничением длины через ПОДСТРОКА.
Разберем по шагам реализацию этого способа:
// 1. Получаем список исходных документов
ЗапросИсточников = Новый Запрос;
ЗапросИсточников.Текст =
"ВЫБРАТЬ
| Оприходование.Ссылка КАК Документ,
| Оприходование.Номер КАК Номер,
| Оприходование.Дата КАК Дата
|ИЗ
| Документ.ОприходованиеТоваров КАК Оприходование";
ТаблицаИсточников = ЗапросИсточников.Выполнить().Выгрузить();
// 2. Добавляем колонки с готовыми строковыми представлениями
ТаблицаИсточников.Колонки.Добавить("СтрокаНомера", Новый ОписаниеТипов("Строка",,,, Новый КвалификаторыСтроки(30)));
ТаблицаИсточников.Колонки.Добавить("СтрокаДаты", Новый ОписаниеТипов("Строка",,,, Новый КвалификаторыСтроки(20)));
Для Каждого Стр Из ТаблицаИсточников Цикл
// Преобразуем дату в строковый формат, как его обычно вводят пользователи
Стр.СтрокаДаты = Формат(Стр.Дата, "ДФ=dd.MM.yyyy");
// Удаляем лидирующие нули из номера при необходимости
Стр.СтрокаНомера = СокрЛП(Стр.Номер);
КонецЦикла;
// 3. Передаем подготовленную таблицу во второй запрос
ЗапросСопоставления = Новый Запрос;
ЗапросСопоставления.Текст =
"ВЫБРАТЬ
| ТЗ.Документ КАК ИсходныйДокумент,
| ТЗ.СтрокаНомера КАК СтрокаНомера,
| ТЗ.СтрокаДаты КАК СтрокаДаты
|ПОМЕСТИТЬ ВТ_Источники
|ИЗ
| &ТаблицаИсточников КАК ТЗ
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| ВТ_Источники.ИсходныйДокумент,
| Поступление.Ссылка КАК ДокументПоступления
|ИЗ
| ВТ_Источники КАК ВТ_Источники
| ЛЕВОЕ СОЕДИНЕНИЕ Документ.ПоступлениеТоваровУслуг КАК Поступление
| ПО ПОДСТРОКА(Поступление.Комментарий, 1, 500) ПОДОБНО ""%"" + ВТ_Источники.СтрокаНомера + ""%""
| И ПОДСТРОКА(Поступление.Комментарий, 1, 500) ПОДОБНО ""%"" + ВТ_Источники.СтрокаДаты + ""%""";
ЗапросСопоставления.УстановитьПараметр("ТаблицаИсточников", ТаблицаИсточников);
Результат = ЗапросСопоставления.Выполнить().Выгрузить();
Если объем документов в базе велик, выполнение соединения через ПОДОБНО в СУБД создаст чрезмерную нагрузку. В этом случае эффективнее получить обе выборки простыми быстрыми запросами и выполнить сопоставление в памяти сервера с помощью функций СтрНайти().
// Выбираем только те документы, где комментарий не пустой
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Поступление.Ссылка КАК Ссылка,
| Поступление.Комментарий КАК Комментарий
|ИЗ
| Документ.ПоступлениеТоваровУслуг КАК Поступление
|ГДЕ
| Поступление.Комментарий <> """"";
ВыборкаПоступлений = Запрос.Выполнить().Выгрузить();
// Сопоставляем в цикле по списку исходных документов
Для Каждого СтрОприходование Из ТаблицаИсточников Цикл
СтрокаПоискаНомера = СтрОприходование.СтрокаНомера;
СтрокаПоискаДаты = СтрОприходование.СтрокаДаты;
Для Каждого СтрПоступление Из ВыборкаПоступлений Цикл
Если СтрНайти(СтрПоступление.Комментарий, СтрокаПоискаНомера) > 0
И СтрНайти(СтрПоступление.Комментарий, СтрокаПоискаДаты) > 0 Тогда
// Связка найдена, фиксируем соответствие
Прервать;
КонецЕсли;
КонецЦикла;
КонецЦикла;
Для исключения регулярных сложностей с неточным поиском по текстовым строкам рекомендуется связать документы на уровне структуры метаданных:
ДокументОснование в документе-приемнике.Такой подход позволяет строить запросы по прямому равенству ссылочных полей, что гарантирует максимальную производительность СУБД и исключает ошибки сопоставления.