В процессе разработки в среде «1С:Предприятие» программисты регулярно сталкиваются с необходимостью текстового анализа номеров документов или артикулов. Типичная бизнес-задача — получить аналитический отчет в разрезе составных частей номера. Например, когда номер документа содержит сложную структуру: буквенный префикс (индекс) переменной длины, уникальный цифровой номер и буквенный суффикс (постфикс), указывающий на тип или статус документа.
Рассмотрим практическую ситуацию. В базе данных существуют документы типа АрхивныйДокумент. Их номера (реквизит РегНомер) могут иметь следующий вид:
Перед нами стоит задача построить отчет, который подсчитает количество документов в разрезе этих индексов (префиксов и суффиксов) и выведет результат в виде удобной кросс-таблицы. В этой статье мы подробно разберем, как решить эту задачу несколькими способами: от классических методов в запросе до современных возможностей платформы 1С (включая регулярные выражения) и лучших практик проектирования высоконагруженных систем.
Если мы ограничены старыми версиями платформы «1С:Предприятие» (ниже 8.3.20), стандартный язык запросов предоставляет нам очень скромный инструментарий для работы со строками. Главным инструментом здесь выступает функция ПОДСТРОКА. Сложность заключается в том, что длина префикса заранее неизвестна (она может составлять как 1 символ, так и 4), а значит, позиция разделителя (дефиса) и цифрового блока динамически меняется.
Проанализируем структуру номера. Мы можем попытаться определить положение дефиса "-" в строке, последовательно проверяя символы на определенных позициях с помощью конструкции ВЫБОР КОГДА. Рассмотрим пример реализации такого запроса:
ВЫБРАТЬ
АрхивныйДокумент.РегНомер КАК РегНомер,
// Определяем префикс документа на основании положения дефиса
ВЫБОР
КОГДА ПОДСТРОКА(АрхивныйДокумент.РегНомер, 2, 1) = "-"
ТОГДА ПОДСТРОКА(АрхивныйДокумент.РегНомер, 1, 1)
КОГДА ПОДСТРОКА(АрхивныйДокумент.РегНомер, 5, 1) = "-"
ТОГДА ПОДСТРОКА(АрхивныйДокумент.РегНомер, 1, 4)
ИНАЧЕ "Неопределен"
КОНЕЦ КАК Префикс,
// Определяем суффикс документа на конце строки
ВЫБОР
КОГДА ПОДСТРОКА(АрхивныйДокумент.РегНомер, 12, 1) = "Т"
И ПОДСТРОКА(АрхивныйДокумент.РегНомер, 13, 1) = "Т"
ТОГДА "ТТ"
КОГДА ПОДСТРОКА(АрхивныйДокумент.РегНомер, 12, 1) = "Т"
И НЕ ПОДСТРОКА(АрхивныйДокумент.РегНомер, 13, 1) = "Т"
ТОГДА "Т"
ИНАЧЕ "Прочее"
КОНЕЦ КАК Суффикс,
АрхивныйДокумент.КолвоКопий КАК КолвоКопий
ИЗ
Документ.АрхивныйДокумент КАК АрхивныйДокумент
Преимущества метода: работает на любых, даже самых старых версиях платформы 1С v8; выполняется полностью на уровне СУБД в рамках одного запроса.
Недостатки метода: крайне низкая гибкость. Если в базе появится префикс другой длины (например, "ОТД-"), или изменится длина цифрового кода номера, данный запрос перестанет выдавать корректные данные. Код запроса быстро становится громоздким и сложным для отладки.
Для упрощения условий в запросе мы можем использовать оператор сравнения по шаблону ПОДОБНО (аналог SQL-оператора LIKE). Это позволяет отсекать неподходящие строки и группировать результаты на основе фиксированных масок номеров.
Если нам достоверно известны шаблоны номеров, мы можем составить лаконичный запрос, определяющий показатели для каждой группы. Посмотрим на следующий пример:
ВЫБРАТЬ
ВЫБОР
КОГДА АрхивныйДокумент.РегНомер ПОДОБНО "Ф-%" ТОГДА "Ф"
КОГДА АрхивныйДокумент.РегНомер ПОДОБНО "Инв.-%" ТОГДА "Инв."
ИНАЧЕ "Прочие"
КОНЕЦ КАК Префикс,
ВЫБОР
КОГДА АрхивныйДокумент.РегНомер ПОДОБНО "%ТТ" ТОГДА "ТТ"
КОГДА АрхивныйДокумент.РегНомер ПОДОБНО "%Т" ТОГДА "Т"
ИНАЧЕ "БезСуффикса"
КОНЕЦ КАК Суффикс,
СУММА(АрхивныйДокумент.КолвоКопий) КАК КолвоКопий
ИЗ
Документ.АрхивныйДокумент КАК АрхивныйДокумент
СГРУППИРОВАТЬ ПО
ВЫБОР
КОГДА АрхивныйДокумент.РегНомер ПОДОБНО "Ф-%" ТОГДА "Ф"
КОГДА АрхивныйДокумент.РегНомер ПОДОБНО "Инв.-%" ТОГДА "Инв."
ИНАЧЕ "Прочие"
КОНЕЦ,
ВЫБОР
КОГДА АрхивныйДокумент.РегНомер ПОДОБНО "%ТТ" ТОГДА "ТТ"
КОГДА АрхивныйДокумент.РегНомер ПОДОБНО "%Т" ТОГДА "Т"
ИНАЧЕ "БезСуффикса"
КОНЕЦ
Важное примечание: Оператор ПОДОБНО транслируется СУБД напрямую. Однако использование символа маски "%" в начале шаблона (например, "%Т") делает невозможным использование индексов СУБД по полю номера, что приведет к полному сканированию таблицы (Table Scan) и снижению быстродействия на больших объемах данных.
Начиная с версии платформы 8.3.20, разработчики 1С получили долгожданный подарок — полноценные функции для работы со строками непосредственно внутри языка запросов и Системы компоновки данных (СКД). Теперь нам доступны функции Лев, Прав, СтрНайти, ДлинаСтроки и функции удаления пробелов.
Благодаря этому нововведению, мы можем динамически находить позицию разделителя "-" и вырезать префикс и суффикс любой длины без жесткого кодирования индексов символов. Разберем этот элегантный подход на примере запроса:
ВЫБРАТЬ
АрхивныйДокумент.РегНомер КАК РегНомер,
// Динамически вычисляем префикс до знака дефиса
ВЫБОР
КОГДА СтрНайти(АрхивныйДокумент.RegНомер, "-") > 0
ТОГДА Лев(АрхивныйДокумент.РегНомер, СтрНайти(АрхивныйДокумент.РегНомер, "-") - 1)
ИНАЧЕ "БезДефиса"
КОНЕЦ КАК Префикс,
// Динамически определяем суффикс (ТТ или Т) на конце строки
ВЫБОР
КОГДА Прав(СокрЛП(АрхивныйДокумент.РегНомер), 2) = "ТТ" ТОГДА "ТТ"
КОГДА Прав(СокрЛП(АрхивныйДокумент.РегНомер), 1) = "Т" ТОГДА "Т"
ИНАЧЕ "Другой"
КОНЕЦ КАК Суффикс
ИЗ
Документ.АрхивныйДокумент КАК АрхивныйДокумент
Этот запрос полностью решает проблему динамической длины префикса. Функция СтрНайти определяет точную позицию дефиса, а функция Лев забирает все символы левее него. Это наиболее гибкий и лаконичный способ решения задачи "на лету" средствами самого запроса.
Если задача допускает обработку данных во временной таблице значений на стороне сервера перед выводом в отчет, то встроенный язык предоставляет невероятно мощный инструмент. Начиная с версии платформы 8.3.23, в 1С реализована встроенная поддержка регулярных выражений (стандарт ICU).
Вместо сложных посимвольных циклов перебора (как в старых решениях) мы можем очистить строку от цифр буквально в одну строчку кода, используя функцию СтрЗаменитьПоРегулярномуВыражению. Рассмотрим, как быстро разобрать строку и получить префикс и суффикс:
// Пример обработки номера документа на сервере
НомерДокумента = "Инв.-000000005ТТ";
// Шаблон "\d+" найдет любую последовательность цифр в номере
// Заменяем все цифры на пустую строку
СтрокаБезЦифр = СтрЗаменитьПоРегулярномуВыражению(НомерДокумента, "\d+", "");
// На выходе получаем чистую маску структуры: "Инв.-ТТ"
// Теперь достаточно разделить строку по символу "-"
ЧастиНомера = СтрРазделить(СтрокаБезЦифр, "-");
Если ЧастиНомера.Количество() >= 2 Тогда
Префикс = ЧастиНомера[0]; // Результат: "Инв."
Суффикс = ЧастиНомера[1]; // Результат: "ТТ"
КонецЕсли;
Этот код работает невероятно быстро, так как регулярные выражения обрабатываются оптимизированным скомпилированным алгоритмом платформы на уровне C++, минуя медленный интерпретатор встроенного языка 1С.
Какими бы удобными ни казались новые функции запросов или регулярные выражения, любой опытный разработчик 1С знает ключевое правило производительности СУБД: избегайте вычислений над строками в условиях соединения, группировки и отбора запросов на реальных базах данных.
Если таблица документов содержит сотни тысяч или миллионы записей, выполнение строковых функций (ПОДСТРОКА, СтрНайти, Прав) в запросе отчета приведет к лавинообразному росту нагрузки на процессор сервера СУБД и полному сканированию таблиц (Table Scan). База данных начнет тормозить, возникнут транзакционные блокировки.
Правильное архитектурное решение заключается в денормализации данных — то есть в расчете префикса и суффикса в момент записи документа и сохранении их в отдельные индексируемые реквизиты. Разберем этот паттерн по шагам:
АрхивныйДокумент два новых реквизита: ПрефиксИндекса (Тип: Строка, Длина: 10) и СуффиксИндекса (Тип: Строка, Длина: 5).Индексировать.ПередЗаписью:
Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
Если Не Отказ Тогда
// Производим разбор номера при изменении или создании
// Используем штатный механизм регулярных выражений платформы 8.3.23+
СтрокаБезЦифр = СтрЗаменитьПоРегулярномуВыражению(ЭтотОбъект.Номер, "\d+", "");
ЧастиНомера = СтрРазделить(СтрокаБезЦифр, "-");
Если ЧастиНомера.Количество() >= 2 Тогда
ЭтотОбъект.ПрефиксИндекса = ЧастиНомера[0];
ЭтотОбъект.СуффиксИндекса = ЧастиНомера[1];
Иначе
ЭтотОбъект.ПрефиксИндекса = "";
ЭтотОбъект.СуффиксИндекса = "";
КонецЕсли;
КонецЕсли;
КонецПроцедуры
После проведения денормализации наш отчет по группировкам префиксов и суффиксов превращается в элементарный, безопасный и мгновенно выполняющийся запрос, который идеально задействует индексы СУБД:
ВЫБРАТЬ
АрхивныйДокумент.ПрефиксИндекса КАК Префикс,
АрхивныйДокумент.СуффиксИндекса КАК Суффикс,
СУММА(АрхивныйДокумент.КолвоКопий) КАК КолвоКопий
ИЗ
Документ.АрхивныйДокумент КАК АрхивныйДокумент
СГРУППИРОВАТЬ ПО
АрхивныйДокумент.ПрефиксИндекса,
АрхивныйДокумент.СуффиксИндекса
Мы детально изучили эволюцию методов разбора строк в системе «1С:Предприятие». Выбор конкретного инструмента зависит от технических требований проекта:
ВЫБОР КОГДА и ПОДСТРОКА с контролем положения разделителей.СтрНайти, Лев и Прав прямо в запросе.