Как разбить строку на подстроки с помощью запроса в 1С:Предприятие

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

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

Принцип работы алгоритма разбиения в запросе

Запрос в 1С не имеет встроенной функции типа СтрРазделить(), поэтому нам приходится имитировать этот процесс согласно шпаргалке по языку запросов. Разберем по шагам общую логику: для того чтобы найти позиции разделителей в строке, нам нужна вспомогательная "таблица чисел" (последовательность индексов от 0 до максимальной длины строки). Соединяя эту таблицу со строкой, мы можем проверить каждый символ по отдельности.

Проанализируем ситуацию: нам нужно превратить строку "а,б,в" в таблицу, где каждая буква — это отдельная запись. Для этого мы сначала генерируем числа, затем определяем позиции запятых и с помощью функции ПОДСТРОКА вырезаем текст между ними.

Шаг 1: Генерация последовательности чисел

Рассмотрим подробнее процесс формирования таблицы индексов через консоль запросов. В запросе 1С нет генератора последовательностей, поэтому мы создаем его вручную через объединение фиксированных значений. Для получения диапазона от 0 до 999 мы используем декартово произведение таблиц.


ВЫБРАТЬ 0 КАК Номер ПОМЕСТИТЬ Цифры
ОБЪЕДИНИТЬ ВЫБРАТЬ 1 ОБЪЕДИНИТЬ ВЫБРАТЬ 2 ОБЪЕДИНИТЬ ВЫБРАТЬ 3
ОБЪЕДИНИТЬ ВЫБРАТЬ 4 ОБЪЕДИНИТЬ ВЫБРАТЬ 5 ОБЪЕДИНИТЬ ВЫБРАТЬ 6
ОБЪЕДИНИТЬ ВЫБРАТЬ 7 ОБЪЕДИНИТЬ ВЫБРАТЬ 8 ОБЪЕДИНИТЬ ВЫБРАТЬ 9;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    Н1.Номер * 100 + Н2.Номер * 10 + Н3.Номер КАК Номер
ПОМЕСТИТЬ Номера
ИЗ
    Цифры КАК Н1
    ЛЕВОЕ СОЕДИНЕНИЕ Цифры КАК Н2 ПО (ИСТИНА)
    ЛЕВОЕ СОЕДИНЕНИЕ Цифры КАК Н3 ПО (ИСТИНА);

В данном фрагменте кода мы сначала создаем временную таблицу Цифры, содержащую значения от 0 до 9. Затем, используя трижды одну и ту же таблицу, мы получаем все возможные комбинации сотен, десятков и единиц. Это дает нам временную таблицу Номера с записями от 0 до 999.

Шаг 2: Подготовка исходной строки

Посмотрим на пример подготовки данных. Для корректной работы алгоритма "поиска конца подстроки" удобно добавить разделитель в самый конец исходного текста. Это гарантирует, что последняя подстрока также будет найдена.


ВЫБРАТЬ
    &ИсходнаяСтрока + &Разделитель КАК Текст
ПОМЕСТИТЬ Строка;

Шаг 3: Поиск позиций и выделение подстрок

Теперь разберем самый сложный этап — основное соединение. Мы должны найти все позиции Н, где находится разделитель (или начало строки), и найти для каждой такой позиции ближайшую следующую позицию К, где также стоит разделитель.


ВЫБРАТЬ
    ПОДСТРОКА(ВЗ.Текст, ВЗ.Н + 1, ВЗ.К - ВЗ.Н - 1) КАК Поле1
ИЗ
    (ВЫБРАТЬ
        Номера.Номер КАК Н,
        МИНИМУМ(Н.Номер) КАК К,
        Строка.Текст КАК Текст
    ИЗ
        Номера КАК Номера
        ВНУТРЕННЕЕ СОЕДИНЕНИЕ Строка КАК Строка
            ПО (ПОДСТРОКА(Строка.Текст, Номера.Номер, 1) ПОДОБНО "[" + &Разделитель + "]"
                ИЛИ Номера.Номер = 0)
        ВНУТРЕННЕЕ СОЕДИНЕНИЕ Номера КАК Н
            ПО (ПОДСТРОКА(Строка.Текст, Н.Номер, 1) ПОДОБНО "[" + &Разделитель + "]")
            И (Н.Номер > Номера.Номер)
    СГРУППИРОВАТЬ ПО
        Номера.Номер,
        Строка.Текст) КАК ВЗ

Проанализируем работу этого запроса. Мы используем функцию ПОДОБНО с конструкцией квадратных скобок "[" + &Разделитель + "]". Это позволяет искать символ-разделитель в тексте. Условие Номера.Номер = 0 позволяет нам зафиксировать начало первой подстроки. С помощью МИНИМУМ(Н.Номер) мы находим позицию ближайшего следующего разделителя, который и определяет конец текущего слова.

Почему запрос может возвращать пустой результат?

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

Важные моменты при отладке:

  1. Проверьте версию платформы. Если у вас установлена 8.3.25.1394, попробуйте обновиться до версии 8.3.25.1445 или более поздней.
  2. Если разделитель фиксирован (например, только запятая), откажитесь от использования ПОДОБНО в пользу прямого сравнения символов. Это работает стабильнее и быстрее.
  3. Обратите внимание на параметр &Разделитель. В консоли запросов убедитесь, что тип параметра — "Строка" и он содержит именно тот символ, который вы ищете.

Оптимизированный подход без оператора ПОДОБНО

Рассмотрим более надежный вариант условия, который снижает вероятность ошибок платформы и повышает производительность. Вместо маски ПОДОБНО будем использовать функцию ПОДСТРОКА для прямого сравнения символа с разделителем.


// Заменим условие ПОДОБНО на:
ПОДСТРОКА(Строка.Текст, Номера.Номер, 1) = &Разделитель

Этот метод предпочтительнее, если разделитель — это один конкретный символ. Это исключает проблемы с экранированием специальных символов (таких как %, _, [), которые оператор ПОДОБНО может интерпретировать неверно.

Современная альтернатива (вне запроса)

Важно помнить, что начиная с версии платформы 8.3.20, разработчикам доступна функция СтрРазделить(). Если ваша задача позволяет получить данные из запроса, а затем обработать их, то гораздо эффективнее сделать это во встроенном языке:


МассивЧастей = СтрРазделить(ИсходнаяСтрока, Разделитель, Ложь);
// Далее массив можно передать обратно в запрос как параметр

Такой подход работает в десятки раз быстрее, чем сложные соединения в запросе, особенно на длинных строках. Используйте разбиение внутри запроса только в тех случаях, когда строка является полем таблицы базы данных и вам нужно выполнить соединение с другими таблицами "на лету" без выгрузки в код.

← На главную