Как самым простым способом передать список значений с формы в запрос 1С?

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

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

В этой статье мы подробно разберем, как решить эту задачу самым простым и изящным способом, рассмотрим различные варианты преобразования данных и изучим важные подводные камни, такие как приведение типов, очистка «грязных» данных и ограничения СУБД на длину списка.

Почему прямая передача строки в запрос не работает?

Проанализируем ситуацию. Допустим, на форме есть реквизит СписокНомеровТекст со строковым значением: "000036176, 000036177, 000036184".

Если мы напишем запрос следующим образом:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	Комплектация.Ссылка КАК Ссылка
    |ИЗ
    |	Документ.Комплектация КАК Комплектация
    |ГДЕ
    |	Комплектация.Номер В (&СписокНомеров)";

Запрос.УстановитьПараметр("СписокНомеров", Объект.СписокНомеровТекст);

СУБД попытается найти документ, номер которого в точности равен всей этой длинной строке целиком, то есть значению "000036176, 000036177, 000036184". Естественно, такого документа не существует. Оператор В в языке запросов 1С требует, чтобы в параметр был передан массив, список значений или таблица значений. Следовательно, нашу исходную строку с разделителями необходимо предварительно преобразовать в массив подстрок.

Решение №1. Использование встроенных функций БСП (Библиотеки стандартных подсистем)

Если вы работаете в любой современной конфигурации (например, 1С:ERP, 1С:Управление торговлей 11, 1С:Комплексная автоматизация или 1С:Бухгалтерия 3.0), то в вашем распоряжении есть мощный инструмент — Библиотека стандартных подсистем. В ней уже реализованы все необходимые функции для парсинга строк.

Самый простой и лаконичный способ — использовать функцию общего назначения. Посмотрим на практический пример реализации:


&НаСервере
Процедура ВыполнитьЗапросНаСервере()
    
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |	Комплектация.Ссылка КАК Ссылка
        |ИЗ
        |	Документ.Комплектация КАК Комплектация
        |ГДЕ
        |	Комплектация.Номер В (&СписокНомеров)";
    
    // Преобразуем строку в массив прямо при установке параметра
    МассивНомеров = ОбщегоНазначенияКлиентСервер.РазложитьСтрокуВМассивПодстрок(Объект.СписокНомеровТекст, ",");
    Запрос.УстановитьПараметр("СписокНомеров", МассивНомеров);
    
    РезультатЗапроса = Запрос.Выполнить();
    // Дальнейшая обработка результата...
    
КонецПроцедуры

Этот метод хорош тем, что он пишется буквально в одну строку. Функция принимает исходный текст и строку-разделитель (в данном случае запятую) и возвращает готовый массив строк, который отлично воспринимается оператором В в запросе.

Решение №2. Использование встроенной функции СтрРазделить (Платформа 8.3)

Если вы разрабатываете конфигурацию с нуля или не хотите привязываться к БСП, в платформе 1С:Предприятие 8.3 есть встроенная функция СтрРазделить. Она работает на уровне платформы и выполняет аналогичную задачу чрезвычайно быстро.

Разберем по шагам, как применить этот метод:


&НаСервере
Процедура ВыполнитьЗапросЧерезСтрРазделить()
    
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |	Комплектация.Ссылка КАК Ссылка
        |ИЗ
        |	Документ.Комплектация КАК Комплектация
        |ГДЕ
        |	Комплектация.Номер В (&СписокНомеров)";
        
    // Третий параметр Истина указывает, что нужно игнорировать пустые подстроки
    МассивНомеров = СтрРазделить(Объект.СписокНомеровТекст, ",", Истина);
    
    Запрос.УстановитьПараметр("СписокНомеров", МассивНомеров);
    РезультатЗапроса = Запрос.Выполнить();
    
КонецПроцедуры

Обратите внимание на третий параметр функции СтрРазделить. Передавая туда значение Истина, мы заставляем систему автоматически удалять из итогового массива пустые строки, которые могли возникнуть, например, если пользователь случайно поставил две запятые подряд или вставил лишний пробел с разделителем.

Повышение отказоустойчивости: Очистка данных перед передачей

На практике пользователи часто копируют списки номеров из внешних таблиц Excel или PDF-файлов. В результате в строке могут оказаться лишние пробелы, кавычки, знаки табуляции или невидимые спецсимволы. Если передать их в запрос как есть, СУБД не сможет сопоставить номера документов.

Рекомендуем выполнять предварительную очистку данных. Нам поможет следующий алгоритм обработки массива в цикле перед передачей в запрос:


&НаСервере
Функция ПодготовитьМассивНомеров(ИсходныйТекст)
    
    // Шаг 1: Удаляем возможные двойные кавычки, которые часто копируются вместе с текстом
    ОчищенныйТекст = СтрЗаменить(ИсходныйТекст, """", "");
    
    // Шаг 2: Разделяем строку по запятой
    ИсходныйМассив = СтрРазделить(ОчищенныйТекст, ",", Истина);
    
    // Шаг 3: Очищаем каждый элемент от пробелов по краям
    Результат = Новый Массив;
    Для Каждого Элемент Из ИсходныйМассив Цикл
        ОчищенноеЗначение = СокрЛП(Элемент);
        Если НЕ ПустаяСтрока(ОчищенноеЗначение) Тогда
            Результат.Добавить(ОчищенноеЗначение);
        КонецЕсли;
    КонецЦикла;
    
    Возврат Результат;
    
КонецФункции

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

Использование функции СтроковыеФункцииКлиентСервер.РазложитьСтрокуВМассивСлов

В современных версиях БСП есть еще более интеллектуальный метод — СтроковыеФункцииКлиентСервер.РазложитьСтрокуВМассивСлов. Проанализируем его преимущества перед стандартным разделением.

Главное отличие этой функции заключается в том, что она в качестве разделителей использует не один конкретный символ (например, запятую), а любые пробельные символы, знаки препинания и переносы строк. Это делает ее идеальным выбором, когда пользователь вводит данные в произвольном формате:


&НаСервере
Процедура ИспользоватьРазложитьСтрокуВМассивСлов()
    
    ТекстПользователя = "000036176; 000036177,  000036184  000036183";
    
    // Разделяет по любым типичным разделителям слов автоматически
    МассивНомеров = СтроковыеФункцииКлиентСервер.РазложитьСтрокуВМассивСлов(ТекстПользователя);
    
    // МассивНомеров будет содержать: "000036176", "000036177", "000036184", "000036183"
    // Все пробелы, точки с запятой и лишние знаки препинания будут проигнорированы
    
КонецПроцедуры

Как мы видим, такой метод обеспечивает максимальную отказоустойчивость программы. Даже если пользователь допустил опечатку, скопировал текст с разделителями разного типа или перенес строки, алгоритм БСП корректно распознает каждое отдельное значение.

Приведение типов: Что делать, если нужны Ссылки, а не Строки?

Предыдущие примеры отлично подходят, если мы ищем документы по строковому реквизиту (например, по номеру, коду или артикулу). Но что делать, если пользователю нужно ввести список уникальных идентификаторов (GUID) или кодов, а в запросе нам требуется выполнить соединение по ссылочному полю (например, по ссылке на справочник Номенклатура)?

В этом случае просто передать массив строк нельзя — запрос выдаст ошибку несовместимости типов. Нам потребуется программно преобразовать строковые представления в ссылки:


&НаСервере
Функция ПолучитьМассивСсылокНоменклатуры(СтрокаСИдентификаторами)
    
    МассивСтрок = СтрРазделить(СтрокаСИдентификаторами, ",", Истина);
    МассивСсылок = Новый Массив;
    
    Для Каждого СтрокаУИД Из МассивСтрок Цикл
        Попытка
            // Преобразуем строку в УникальныйИдентификатор и получаем ссылку
            УИД = Новый УникальныйИдентификатор(СокрЛП(СтрокаУИД));
            СсылкаНаНоменклатуру = Справочники.Номенклатура.ПолучитьСсылку(УИД);
            МассивСсылок.Добавить(СсылкаНаНоменклатуру);
        Исключение
            // Обработка некорректно введенного GUID
            ЗаписьЖурналаРегистрации("ПреобразованиеТипов", УровеньЖурналаРегистрации.Предупреждение,,, 
                "Не удалось преобразовать строку в GUID: " + СтрокаУИД);
        КонецПопытки;
    КонецЦикла;
    
    Возврат МассивСсылок;
    
КонецФункции

Таким образом, мы передаем в запрос массив полноценных ссылок, и фильтрация по ссылочному полю отработает корректно и максимально быстро.

Ограничения СУБД на количество элементов в операторе В

Важно помнить об архитектурных особенностях систем управления базами данных (особенно Microsoft SQL Server). При использовании конструкции Номер В (&Список) СУБД разворачивает этот параметр в длинный перечень условий. Если пользователь передаст в текстовое поле список из нескольких тысяч элементов (например, более 1000), запрос может:

  1. Завершиться критической ошибкой переполнения стека парсера SQL-сервера.
  2. Работать крайне медленно из-за неоптимального плана выполнения запроса.

Если предполагается обработка действительно больших списков, правильнее отказаться от оператора В в пользу временных таблиц. Рассмотрим, как это реализовать:


&НаСервере
Процедура ВыполнитьЗапросЧерезВременнуюТаблицу(МассивЗначений)
    
    МенеджерВТ = Новый МенеджерВременныхТаблиц;
    Запрос = Новый Запрос;
    Запрос.МенеджерВременныхТаблиц = МенеджерВТ;
    
    // Создаем временную таблицу для хранения списка значений
    Запрос.Текст = 
        "ВЫБРАТЬ
        |	&МассивЗначений КАК Значение
        |ПОМЕСТИТЬ ВТ_Фильтр";
    
    Запрос.УстановитьПараметр("МассивЗначений", МассивЗначений);
    Запрос.Выполнить();
    
    // Теперь используем внутреннее соединение вместо оператора "В"
    Запрос.Текст = 
        "ВЫБРАТЬ
        |	Комплектация.Ссылка КАК Ссылка
        |ИЗ
        |	Документ.Комплектация КАК Комплектация
        |	ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_Фильтр КАК ВТ_Фильтр
        |		ПО Комплектация.Номер = ВТ_Фильтр.Значение";
        
    Результат = Запрос.Выполнить().Выгрузить();
    
КонецПроцедуры

Такой подход гарантирует стабильную работу системы и оптимальную скорость выполнения запроса вне зависимости от объема передаваемых пользователем данных.

Продвинутый парсинг с помощью регулярных выражений

Иногда пользователи вводят списки, разделяя элементы хаотично: где-то запятой, где-то точкой с запятой, а где-то просто переносом строки. Чтобы не писать громоздкие цепочки функций СтрЗаменить, мы можем прибегнуть к возможностям регулярных выражений через COM-объект VBScript.RegExp.

Давайте посмотрим, как можно извлечь все слова или числа из сложного текста одной регулярной маской:


&НаСервере
Функция ИзвлечьЗначенияРегулярнымВыражением(ИсходныйТекст)
    
    МассивРезультатов = Новый Массив;
    
    Попытка
        RegExp = Новый COMОбъект("VBScript.RegExp");
        RegExp.Global = Истина;
        RegExp.IgnoreCase = Истина;
        RegExp.Multiline = Истина;
        
        // Шаблон ищет любые последовательности букв и цифр, игнорируя спецсимволы
        RegExp.Pattern = "[a-zA-Z0-9а-яА-Я0-9_]+"; 
        
        Matches = RegExp.Execute(ИсходныйТекст);
        Для Каждого Match Из Matches Цикл
            МассивРезультатов.Добавить(Match.Value);
        КонецЦикла;
    Исключение
        // Резервный вариант на случай работы на сервере под Linux
        МассивРезультатов = СтрРазделить(ИсходныйТекст, ",", Истина);
    КонецПопытки;
    
    Возврат МассивРезультатов;
    
КонецФункции

Этот метод чрезвычайно гибок, так как он автоматически отсекает любые знаки препинания, скобки, кавычки и пробелы, возвращая массив чистых значений.

Альтернатива через Систему компоновки данных (СКД)

Если задача заключается исключительно в том, чтобы дать пользователю возможность фильтровать отчет по списку значений, то лучшим решением будет использование встроенного функционала СКД. Нам не придется писать программный код парсинга вообще.

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

Подводя итоги, отметим, что для большинства стандартных задач в управляемом приложении 1С достаточно использовать связку строкового реквизита на форме и платформенной функции СтрРазделить или метода БСП. При работе с большими объемами данных всегда отдавайте предпочтение временным таблицам, чтобы избежать проблем с производительностью СУБД.

← На главную