Работа с массивами в запросах 1С — одна из тех тем, которые разделяет разработчиков на два лагеря: одни используют их ежедневно и не видят сложностей, другие годами обходятся без них, считая избыточным инструментом. Между тем, умение грамотно передавать массивы как параметры запросов открывает возможности для гибкой фильтрации данных, динамического формирования условий и даже оптимизации производительности в сложных отчетах. Но здесь кроется и главная ловушка: неверное применение массивов может не просто замедлить выполнение запроса, а привести к непредсказуемым ошибкам или некорректным результатам.

В этой статье мы разберём три критических аспекта работы с массивами в запросах 1С, которые редко освещают в стандартной документации: как именно платформа интерпретирует массивы в условиях ГДЕ, почему иногда лучше использовать временные таблицы вместо массивов, и как обойти ограничения при работе с большими наборами данных (10 000+ элементов). Особое внимание уделим типичным ошибкам, которые допускают даже опытные программисты — например, попытке передать массив объектов вместо массива значений или игнорированию особенностей сравнения NULL в условиях.

1. Синтаксис передачи массива в запрос: базовые правила

Начнём с самого простого: как технически передать массив в запрос. В 1С:Предприятие 8.3 для этого используется конструкция В() (или IN в англоязычной нотации), которая позволяет указать список значений для сравнения. Например, чтобы выбрать документы с определёнными номерами, можно написать:

Запрос = Новый Запрос;

Запрос.Текст =

"ВЫБРАТЬ

| Номенклатура.Наименование КАК Наименование

|ИЗ

| Справочник.Номенклатура КАК Номенклатура

|ГДЕ

| Номенклатура.Ссылка В(&МассивСсылок)";

МассивСсылок = Новый Массив;

МассивСсылок.Добавить(Справочники.Номенклатура.НайтиПоНаименованию("Стул"));

МассивСсылок.Добавить(Справочники.Номенклатура.НайтиПоНаименованию("Стол"));

Запрос.УстановитьПараметр("МассивСсылок", МассивСсылок);

Результат = Запрос.Выполнить();

Важно понимать, что платформа автоматически преобразует массив в список значений при передаче в запрос. Это означает, что:

  • 🔹 Массив должен содержать данные одного типа (например, только ссылки, только строки или только числа). Смешанные типы приводят к ошибке.
  • 🔹 Если массив пуст, условие В() вернёт ЛОЖЬ для всех строк (т.е. результат будет пустым). Это поведение отличается от SQL, где пустой IN () часто считается синтаксической ошибкой.
  • 🔹 Для массивов объектов (например, Массив Из ДокументОбъект.СтрокаТабличнойЧасти) требуется предварительное извлечение значений или ссылок.
⚠️ Внимание: При передаче массива ссылок на объекты убедитесь, что все элементы принадлежат одной базе данных. Попытка использовать ссылки из другой базы (например, через RAS или HTTP-сервисы) приведёт к ошибке выполнения запроса.

2. Массив vs временная таблица: когда что использовать

Один из самых частых вопросов: стоит ли передавать массив напрямую в запрос или лучше загрузить его во временную таблицу? Ответ зависит от трёх факторов:

Критерий Массив в условии В() Временная таблица
Размер данных До 1 000 элементов От 1 000 элементов
Производительность Быстрее для маленьких наборов Эффективнее при сложных соединениях
Гибкость Ограничен условия ГДЕ Позволяет использовать СОЕДИНЕНИЕ, агрегацию
Память Низкое потребление Высокое при больших объёмах

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

Рассмотрим практический пример с временной таблицей:

Запрос = Новый Запрос;

Запрос.Текст =

"ВЫБРАТЬ

| Товары.Наименование,

| Товары.Цена

|ИЗ

| Документ.ПоступлениеТоваров.Товары КАК Товары

| ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТКакТаблица КАК Фильтр

| ПО Товары.Номенклатура = Фильтр.Ссылка";

// Создаём временную таблицу

ВТ = Новый ВременнаяТаблица;

ВТ.Колонки.Добавить("Ссылка", Новый ОписаниеТипов("СправочникСсылка.Номенклатура"));

// Заполняем данными из массива

Для Каждого Элемент Из МассивНоменклатуры Цикл

Строка = ВТ.Добавить();

Строка.Ссылка = Элемент;

КонецЦикла;

Запрос.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;

Запрос.МенеджерВременныхТаблиц.ДобавитьВременнуюТаблицу(ВТ, "ВТКакТаблица");

Результат = Запрос.Выполнить();

📊 Какой способ фильтрации вы используете чаще?
Массив в условии В()
Временные таблицы
Оба варианта в зависимости от задачи
Не использую массивы в запросах

3. Типичные ошибки и как их избежать

Даже опытные разработчики иногда сталкиваются с неожиданными проблемами при работе с массивами в запросах. Вот три самых распространённых ошибки и способы их решения:

  • 🚨 Ошибка типа данных: Попытка передать в запрос массив, содержащий разные типы (например, строки и числа). Решение — предварительная проверка типов или приведение к общему типу с помощью ЗначениеВТип().
  • 🚨 Пустой массив: Запрос с условием В(&ПустойМассив) вернёт пустой результат, что может быть неочевидно. Решение — явная проверка Массив.Количество() > 0 перед выполнением.
  • 🚨 NULL-значения: Если массив содержит NULL, сравнение В() может работать некорректно. Решение — явная обработка ЕСТЬNULL или фильтрация массива перед передачей.

Особенно коварна ошибка с NULL. Например, следующий код может вернуть неожиданные результаты:

Массив = Новый Массив;

Массив.Добавить(Неопределено); // NULL

Массив.Добавить(Справочники.Номенклатура.ПустаяСсылка());

Запрос.Текст = "ВЫБРАТЬ ... ГДЕ Ссылка В(&Массив)";

// В результате могут быть исключены даже не-NULL значения!

⚠️ Внимание: При работе с массивами ссылок на объекты (например, документы или справочники) всегда проверяйте, что ссылки существуют в базе. Передача несуществующих ссылок не вызовет ошибку, но может исказить результаты запроса.
Почему запрос с массивом работает медленно?

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

4. Оптимизация производительности: массивы и большие данные

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

  1. Разбиение массива: Если массив содержит 10 000 элементов, разбейте его на части по 1 000 и выполните несколько запросов с объединением результатов.
  2. Использование временных таблиц: Как показано ранее, загрузка массива во временную таблицу часто работает быстрее за счёт оптимизации движка запросов.
  3. Индексирование: Убедитесь, что поля, по которым идёт фильтрация массивом, проиндексированы в базе данных. Например, для справочников проверьте наличие индекса по полю Ссылка.

Пример разбиения массива на части:

РезультатОбщий = Новый ТаблицаЗначений;

РазмерЧасти = 1000;

Для Позиция = 0 По Массив.ВГраница() Шаг РазмерЧасти Цикл

ЧастьМассива = Массив.ПолучитьОбъект(Позиция, Минимальное(Позиция + РазмерЧасти, Массив.ВГраница()));

Запрос.УстановитьПараметр("Часть", ЧастьМассива);

ЧастьРезультата = Запрос.Выполнить().Выгрузить();

РезультатОбщий.Объединить(ЧастьРезультата);

КонецЦикла;

Ещё один нюанс: если вы работаете с управляемыми формами и передаёте массив из клиентской части на сервер, убедитесь, что данные передаются по значению, а не по ссылке. В противном случае может возникнуть избыточный сетевой трафик:

// На клиенте:

Параметры = Новый Структура("Массив", Массив.Скопировать());

// На сервере:

Запрос.УстановитьПараметр("Массив", Параметры.Массив);

💡

Для массивов числовых значений (например, ID документов) рассмотрите возможность использования диапазонов вместо точных совпадений. Например, условие ГДЕ Номер МЕЖДУ 1000 И 2000 выполнится быстрее, чем ГДЕ Номер В(&МассивИз1000Чисел).

5. Продвинутые техники: динамическое формирование запросов

Иногда требуется не просто передать массив в запрос, а динамически формировать сам запрос на основе содержимого массива. Например, если нужно построить условие с разными операторами для разных элементов. Для этого можно использовать СтрокаЗапроса и функции работы со строками.

Пример: формирование условия ГДЕ с разными операторами (=, <>) для элементов массива:

Условия = Новый Массив;

Для Каждого Элемент Из МассивУсловий Цикл

Если Элемент.Тип = ТипУсловия.Равно Тогда

Условия.Добавить(СтрШаблон("Поле = %1", Элемент.Значение));

ИначеЕсли Элемент.Тип = ТипУсловия.НеРавно Тогда

Условия.Добавить(СтрШаблон("Поле <> %1", Элемент.Значение));

КонецЕсли;

КонецЦикла;

ТекстЗапроса = "ВЫБРАТЬ ... ГДЕ " + СтрСоединить(Условия, " И ");

Запрос.Текст = ТекстЗапроса;

Однако такой подход требует осторожности:

  • 🔧 Всегда экранируйте значения, чтобы избежать SQL-инъекций (даже в 1С это актуально при работе с внешними СУБД).
  • 🔧 Проверяйте длину итогового запроса — некоторые СУБД (например, PostgreSQL) имеют ограничения на длину SQL-запроса.
  • 🔧 Для сложных условий рассмотрите использование ПОМЕСТИТЬ ВТ вместо динамического формирования текста.

Экранированы все специальные символы в значениях|Проверена длина итогового запроса|Учтёны ограничения СУБД на количество параметров|Протестировано на пустом массиве-->

6. Альтернативные подходы: когда массивы не подходят

Иногда передача массива в запрос — не лучшее решение. Рассмотрим альтернативы:

  1. Подзапросы: Если массив формируется на основе данных из базы, можно заменить его подзапросом. Например, вместо передачи массива ссылок на контрагентов используйте:
    ГДЕ Контрагент В (
    

    ВЫБРАТЬ КонтрагентыСписок.Ссылка

    ИЗ Справочник.Контрагенты КАК КонтрагентыСписок

    ГДЕ КонтрагентыСписок.ПометкаУдаления = ЛОЖЬ

    )

  2. Глобальные параметры: Для часто используемых фильтров (например, "текущий пользователь") целесообразно завести константу или параметр сеанса вместо передачи массива.
  3. Внешние источники: Если данные хранятся во внешней системе (например, Excel или JSON), рассмотрите загрузку их во временную таблицу через ЗагрузкаДанных.

Пример с подзапросом вместо массива:

Запрос.Текст =

"ВЫБРАТЬ

| Документы.Номер,

| Документы.Дата

|ИЗ

| Документ.РеализацияТоваровУслуг КАК Документы

|ГДЕ

| Документы.Контрагент В (

| ВЫБРАТЬ Контрагенты.Ссылка

| ИЗ Справочник.Контрагенты КАК Контрагенты

| ГДЕ Контрагенты.Группа = &ГруппаКонтрагентов

| )";

Запрос.УстановитьПараметр("ГруппаКонтрагентов", Справочники.ГруппыКонтрагентов.Оптовики);

Результат = Запрос.Выполнить();

⚠️ Внимание: Подзапросы могут значительно замедлить выполнение, если внутренний запрос возвращает большое количество строк. В таких случаях временная таблица часто оказывается эффективнее.

7. Особенности работы в разных версиях 1С

Поведение массивов в запросах может отличаться в зависимости от версии платформы и конфигурации. Вот ключевые различия:

Версия платформы Особенности работы с массивами
8.2 Ограничение на размер массива в условии В() — до 1 000 элементов. Превышение приводит к ошибке.
8.3.6–8.3.12 Поддержка массивов до 10 000 элементов, но производительность падает после 1 000.
8.3.13+ Оптимизированная обработка больших массивов, поддержка МАССИВ() в тексте запроса.
Управляемые формы При передаче массива с клиента на сервер требуется явное копирование (.Скопировать()).

В версиях 8.3.13 и новее появилась возможность использовать функцию МАССИВ() непосредственно в тексте запроса, что упрощает работу с небольшими статическими наборами данных:

Запрос.Текст =

"ВЫБРАТЬ

| Товары.Наименование

|ИЗ

| Справочник.Номенклатура КАК Товары

|ГДЕ

| Товары.ВидНоменклатуры В МАССИВ(Перечисление.ВидыНоменклатуры.Товар, Перечисление.ВидыНоменклатуры.Услуга)";

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

💡

В версиях 8.3.13+ для статических массивов удобно использовать функцию МАССИВ() прямо в тексте запроса, что избавляет от необходимости передавать параметры.

FAQ: Частые вопросы по работе с массивами в запросах 1С

Можно ли передать в запрос массив структур или объектов?

Нет, напрямую — нельзя. Запрос 1С работает только с простыми типами данных (числа, строки, даты, ссылки, булевы значения). Если вам нужно отфильтровать данные по сложному критерию (например, по нескольким полям объекта), предварительно извлеките нужные значения в массив простых типов или используйте временную таблицу.

Пример ошибки:

МассивОбъектов = Новый Массив;

МассивОбъектов.Добавить(Документы.ЗаказПокупателя.СоздатьДокумент());

Запрос.УстановитьПараметр("Объекты", МассивОбъектов); // Ошибка!

Почему запрос с массивом из 5 000 элементов выполняется очень долго?

Это связано с особенностями оптимизации запросов в 1С. При большом количестве элементов в условии В() платформа может:

  • 🔍 Не использовать индексы (переключаться на полное сканирование таблицы).
  • 🔍 Формировать чрезмерно сложный план выполнения.

Решения:

  1. Разбейте массив на части по 500–1 000 элементов и выполните несколько запросов.
  2. Загрузите массив во временную таблицу и используйте СОЕДИНЕНИЕ.
  3. Проверьте наличие индексов на фильтруемых полях.

Как передать массив дат в запрос?

Массив дат передаётся так же, как и любой другой массив простых типов. Главное — убедиться, что все элементы имеют тип Дата. Пример:

МассивДат = Новый Массив;

МассивДат.Добавить(Дата(2023, 1, 1));

МассивДат.Добавить(Дата(2023, 1, 15));

Запрос.Текст = "ВЫБРАТЬ ... ГДЕ Дата ДОКУМЕНТА В(&МассивДат)";

Запрос.УстановитьПараметр("МассивДат", МассивДат);

Обратите внимание: если в массиве есть Неопределено или пустые даты, они будут проигнорированы при сравнении.

Можно ли использовать массив в условии ПОДОБНО?

Нет, конструкция В() работает только с точными совпадениями (=). Для условия ПОДОБНО придётся либо:

  • 🔠 Формировать динамический текст запроса с многократным ИЛИ.
  • 🔠 Использовать временную таблицу с последующим соединением.

Пример с динамическим формированием:

УсловияПоиска = Новый Массив;

Для Каждого Шаблон Из МассивШаблонов Цикл

УсловияПоиска.Добавить(СтрШаблон("Поле ПОДОБНО '%%1%%'", Шаблон));

КонецЦикла;

Запрос.Текст = "ВЫБРАТЬ ... ГДЕ " + СтрСоединить(УсловияПоиска, " ИЛИ ");

Как передать массив в запрос, если он формируется в цикле?

Если массив формируется динамически (например, в цикле по табличной части документа), важно:

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

Пример корректного подхода:

МассивФильтра = Новый Массив;

Для Каждого Строка Из Документ.Товары Цикл

Если Строка.Количество > 10 Тогда

МассивФильтра.Добавить(Строка.Номенклатура);

КонецЕсли;

КонецЦикла;

Запрос.УстановитьПараметр("Фильтр", МассивФильтра); // Массив уже полностью сформирован