При решении задач нормализации данных и поиска некорректно заполненных справочников часто возникает необходимость проанализировать строковые реквизиты. Типичный пример — поиск записей, где в поле ФИО ошибочно внесены данные сразу нескольких человек через пробел, либо требуется отобрать строки, содержащие заданное количество слов.
Разберем, почему стандартные строковые функции не работают напрямую в запросе, какие существуют способы подсчета пробелов средствами языка запросов 1С и как правильно решить задачу на практике.
Встроенный язык 1С предоставляет богатый набор методов для работы с текстом (СтрНайти, СтрЧислоВхождений, СтрЗаменить, СтрРазделить). Однако в языке запросов 1С большинство из этих функций недоступны, так как текст запроса транслируется в SQL-диалект конкретной СУБД, где действуют строгие ограничения платформы.
Начиная с версии платформы 8.3.20, в язык запросов были добавлены базовые строковые операции: ДЛИНАСТРОКИ, СОКРЛ, СОКРП, СОКРЛП, ЛЕВ, ПРАВ. Тем не менее, специализированной функции для подсчета количества конкретных символов в запросе по-прежнему нет. Выясним, какими способами можно обойти это ограничение.
Если нам необходимо отобрать строки, в которых содержится определенное минимальное количество пробелов (например, от 4 и более), проще всего воспользоваться оператором сопоставления с шаблоном ПОДОБНО.
Посмотрим на пример отбора записей, содержащих как минимум 4 пробела:
ВЫБРАТЬ
ФизическиеЛица.Ссылка КАК Ссылка,
ФизическиеЛица.Наименование КАК Наименование
ИЗ
Справочник.ФизическиеЛица КАК ФизическиеЛица
ГДЕ
ФизическиеЛица.Наименование ПОДОБНО "% % % % %"
Если требуется получить вычисляемое числовое поле с примерным количеством пробелов (например, до 4 включительно), применим конструкцию ВЫБОР КОГДА ... ТОГДА, проверяя шаблоны от большего количества вхождений к меньшему:
ВЫБРАТЬ
ФизическиеЛица.Ссылка КАК Ссылка,
ФизическиеЛица.Наименование КАК Наименование,
ВЫБОР
КОГДА ФизическиеЛица.Наименование ПОДОБНО "% % % %" ТОГДА 4
КОГДА ФизическиеЛица.Наименование ПОДОБНО "% % %" ТОГДА 3
КОГДА ФизическиеЛица.Наименование ПОДОБНО "% %" ТОГДА 2
КОГДА ФизическиеЛица.Наименование ПОДОБНО "% %" ТОГДА 1
ИНАЧЕ 0
КОНЕЦ КАК КоличествоПробелов
ИЗ
Справочник.ФизическиеЛица КАК ФизическиеЛица
Важный нюанс: шаблон вида "% % %" реагирует на любые пробелы, включая подряд идущие (двойные пробелы между словами или пробелы в начале/конце строки). Если нужно посчитать именно разделенные словами фрагменты, шаблон следует уточнить с помощью конструкции исключения символов [^ ]:
ГДЕ
ФизическиеЛица.Наименование ПОДОБНО "%[^ ]% [^ ]% [^ ]% [^ ]%"
Такой шаблон гарантирует, что между каждым пробелом присутствует как минимум один непробельный символ.
Когда требуется вычислить точное произвольное количество пробелов (или любых других символов) исключительно средствами запроса, применяют соединение с генератором чисел (декартово произведение).
Разберем по шагам логику этого подхода:
ПОДСТРОКА проверяем каждый символ строки: если он равен пробелу, учитываем его.КОЛИЧЕСТВО.Посмотрим на пример реализации:
ВЫБРАТЬ 1 КАК Число ПОМЕСТИТЬ ВТ_Цифры
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 2
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 3
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 4
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 5
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ФизЛица.Ссылка КАК Ссылка,
ФизЛица.Наименование КАК Наименование,
КОЛИЧЕСТВО(ВТ_Цифры.Число) КАК КоличествоПробелов
ИЗ
Справочник.ФизическиеЛица КАК ФизЛица
ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_Цифры КАК ВТ_Цифры
ПО ВТ_Цифры.Число <= ДЛИНАСТРОКИ(ФизЛица.Наименование)
И ПОДСТРОКА(ФизЛица.Наименование, ВТ_Цифры.Число, 1) = " "
СГРУППИРОВАТЬ ПО
ФизЛица.Ссылка,
ФизЛица.Наименование
Если задача выполняется в обработке или внешнем отчете и жесткого требования сделать все исключительно одним SQL-запросом нет, наиболее производительным и надежным решением является выборка данных запросом с последующим вычислением количества пробелов на встроенном языке 1С.
Рассмотрим несколько вариантов расчета на встроенном языке:
СтрЧислоВхождений:
КоличествоПробелов = СтрЧислоВхождений(Выборка.Наименование, " ");
КоличествоПробелов = СтрДлина(Выборка.Наименование) - СтрДлина(СтрЗаменить(Выборка.Наименование, " ", ""));
МассивСлов = СтрРазделить(Выборка.Наименование, " ", Ложь);
// Ложь исключает пустые элементы при множественных пробелах подряд
Если МассивСлов.Количество() > 3 Тогда
// Обнаружено более трех слов в одном реквизите
КонецЕсли;
Проанализируем предметную задачу: поиск записей, где в одном реквизите сохранено несколько ФИО. Опираться исключительно на пробелы не всегда корректно, так как стандартное ФИО может содержать:
Для качественного анализа базы рекомендуется комбинировать поиск по пробелам с отбором по другим символам-разделителям (ПОДОБНО "%,%", ПОДОБНО "%;%"), а также использовать типовые механизмы БСП из общего модуля ФизическиеЛицаКлиентСервер для корректного разбора ФИО на составные части.