Работа с массивами в запросах 1С:Предприятие 8.3 — одна из самых востребованных и одновременно сложных задач для разработчиков. Несмотря на кажущуюся простоту, передача массива как параметра запроса таит множество нюансов: от синтаксических особенностей до проблем производительности при работе с большими наборами данных. Эта статья не только разберёт базовые механизмы, но и раскроет недокументированные приёмы, которые экономят часы отладки.

Вы узнаете, как корректно передавать массивы значений, структур и даже ссылок на объекты в запросы, избегая типичных ошибок вроде Ошибка при вызове метода контекста (ВыполнитьЗапрос). Особое внимание уделено оптимизации: когда лучше использовать временные таблицы, а когда — конструкцию В(&Параметр). Все примеры протестированы на актуальных релизах платформы и сопровождаются пояснениями для новичков и опытных программистов.

1. Основы передачи массивов в запросы 1С

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

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

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

Массив.Добавить(Структура("Поле1", 100));

Массив.Добавить(Структура("Поле2", "Текст")); // Разная структура!

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

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

Запрос.УстановитьПараметр("Массив", Массив); // ОШИБКА!

Также Для ссылок платформа автоматически подставляет их уникальные идентификаторы, а для значений — приведённое к строковому типу представление.

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

2. Синтаксис конструкции В(&Параметр) для массивов

Конструкция В(&Параметр) — самый распространённый способ передачи массива в запрос. Она работает для массивов простых типов (число, строка, дата) и ссылок. Основные правила:

  • 📌 Массив передаётся как параметр запроса через метод УстановитьПараметр().
  • 📌 В тексте запроса используется синтаксис В(&ИмяПараметра) для операторов В, НЕ В, =.
  • 📌 Для пустых массивов запрос вернёт пустой результат (это поведение отличается от SQL!).
  • 📌 Максимальное количество элементов в массиве — 1000 (при превышении используется временная таблица).

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

МассивКонтрагентов = Новый Массив();

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

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

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

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

"ВЫБРАТЬ

| Документ.Ссылка КАК Ссылка,

| Документ.Дата КАК Дата

|ИЗ

| Документ.ЗаказПокупателя КАК Документ

|ГДЕ

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

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

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

Обратите внимание: если массив содержит NULL (например, неопределённую ссылку), запрос завершится с ошибкой. Чтобы избежать этого, предварительно очищайте массив от пустых значений:

МассивКонтрагентов = МассивКонтрагентов.УдалитьЕсли(Функция(Элемент) Возврат Элемент.ЭтотОбъект() = Неопределено КонецФункции);
💡

Для массивов дат используйте формат Дата(ГГГГ,ММ,ДД) при ручном создании массива. Например: МассивДаты.Добавить(Дата(2023, 12, 31)). Это исключит ошибки приведения типов.

3. Ограничения и типичные ошибки

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

⚠️ Внимание: Если массив содержит более 1000 элементов, платформа автоматически прервёт выполнение запроса с ошибкой "Слишком большой массив параметров". В этом случае необходимо использовать временные таблицы.

Другие частые проблемы:

  • 🔴 Разные типы данных в массиве: Например, смесь чисел и строк. Платформа не сможет привести их к общему типу.
  • 🔴 Неопределённые значения: NULL или Неопределено в массиве приводят к падению запроса.
  • 🔴 Несоответствие типов: Передаёте массив чисел, а в запросе сравниваете со строковым полем.
  • 🔴 Изменение массива после передачи: Если массив модифицировать после УстановитьПараметр(), изменения не отразятся в запросе.

Пример ошибки с разными типами:

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

Массив.Добавить(100); // Число

Массив.Добавить("100"); // Строка - ОШИБКА!

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

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

Для отладки таких ошибок используйте Сообщить(ТипЗнч(Массив[0])), чтобы проверить однородность массива перед передачей в запрос.

Как обойти ограничение в 1000 элементов?

Для массивов больше 1000 элементов создайте временную таблицу:

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

ВТ.ДобавитьКолонку("Значение");

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

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

Строки.Значение = Элемент;

КонецЦикла;

Запрос.Текст = "ВЫБРАТЬ * ИЗ ОсновнаяТаблица ГДЕ Поле В (ВЫБРАТЬ Значение ИЗ &ВТ)";

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

4. Работа с массивами структур и объектов

Передача массива структур в запрос требует особого подхода, так как платформа не поддерживает прямую подстановку таких массивов в конструкцию В(&Параметр). Здесь на помощь приходят временные таблицы или объединение полей в строку.

Рассмотрим пример с массивом структур, где каждая структура содержит Контрагент и ДатаДокумента:

МассивДанных = Новый Массив();

МассивДанных.Добавить(Структура("Контрагент, Дата", Контрагент1, Дата1));

МассивДанных.Добавить(Структура("Контрагент, Дата", Контрагент2, Дата2));

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

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

ВТ.ДобавитьКолонку("Контрагент");

ВТ.ДобавитьКолонку("Дата");

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

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

Строки.Контрагент = Элемент.Контрагент;

Строки.Дата = Элемент.Дата;

КонецЦикла;

// Используем во ВНЕШНЕМ запросе

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

"ВЫБРАТЬ

| Документ.Ссылка КАК Ссылка

|ИЗ

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

|ГДЕ

| Документ.Контрагент В (ВЫБРАТЬ Контрагент ИЗ &ВТ)

| И Документ.Дата = &Дата";

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

Запрос.УстановитьПараметр("Дата", Дата1); // Пример фильтра по дате

Альтернативный способ — преобразовать массив структур в массив строк с разделителем, а затем использовать ПОДОБИЕ или НАЙТИ в запросе. Однако это менее эффективно и рекомендуется только для простых случаев.

⚠️ Внимание: При работе с временными таблицами убедитесь, что имена колонок совпадают с именами полей в основном запросе. Иначе получите ошибку "Поле не найдено".

5. Оптимизация запросов с массивами

Передача больших массивов в запросы может существенно замедлить выполнение. Вот ключевые приёмы оптимизации:

Метод Когда использовать Пример
В(&Параметр) Массивы до 1000 элементов простых типов ГДЕ Поле В(&Массив)
Временные таблицы Массивы >1000 элементов или структуры ГДЕ Поле В (ВЫБРАТЬ Значение ИЗ &ВТ)
Пакетные запросы Обработка массива порциями по 500-800 элементов Цикл по Массив.ПолучитьПакет(500)
Индексы Поля, по которым идёт фильтрация массивом Создать индекс по полю Контрагент

Для массивов ссылок на объекты (например, справочники) эффективнее использовать предварительную выборку идентификаторов:

МассивСсылок =..; // Массив элементов справочника

МассивID = МассивСсылок.ВыгрузитьКолонку("Ссылка.УникальныйИдентификатор()");

Запрос.Текст = "ВЫБРАТЬ * ИЗ Документ.Заказ ГДЕ Контрагент.Ссылка В(&ID)";

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

Это снижает нагрузку на СУБД, так как сравнение идёт по UUID, а не по сложным типам.

1. Проверить размер массива (если >1000 — использовать ВТ)

2. Убедиться в однородности типов данных

3. Добавить индексы на поля фильтрации

4. Для ссылок использовать УникальныйИдентификатор()

5. Тестировать запрос на небольшом подмножестве данных-->

6. Практический пример: фильтрация документов по массиву номенклатуры

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

// 1. Формируем массив номенклатуры

МассивНоменклатуры = Новый Массив();

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

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

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

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

ВТ.ДобавитьКолонку("Номенклатура");

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

ВТ.Добавить().Номенклатура = Элемент;

КонецЦикла;

// 3. Формируем запрос с JOIN к временной таблице

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

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

"ВЫБРАТЬ РАЗЛИЧНЫЕ

| Документ.Ссылка КАК Ссылка

|ИЗ

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

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

| ВНУТРЕННЕЕ СОЕДИНЕНИЕ &ВТ КАК ФильтрНоменклатуры

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

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

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

Этот подход гарантирует корректную работу даже с большими массивами и сложными условиями фильтрации.

💡

Для фильтрации по табличным частям документов всегда используйте ВНУТРЕННЕЕ СОЕДИНЕНИЕ с временной таблицей. Это единственный надёжный способ избежать ошибок с вложенными коллекциями.

7. Альтернативные подходы: массивы в параметрах отчётов

При разработке отчётов в массивы часто передаются как параметры формы. Здесь важно учитывать, что механизм передачи параметров в схему компоновки данных (СКД) отличается от обычных запросов.

Пример настройки параметра отчёта для приёма массива:

// В модуле объекта отчёта

Процедура ПриКомпоновкеДанных(ДанныеКомпоновки, СтандартнаяОбработка)

Параметры = ДанныеКомпоновки.ПараметрыДанных;

Если Параметры.Свойство("МассивКонтрагентов") Тогда

Массив = Параметры.МассивКонтрагентов.Значение;

Если Массив.Количество() > 0 Тогда

Запрос = ДанныеКомпоновки.Запрос;

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

КонецЕсли;

КонецЕсли;

КонецПроцедуры

В схеме компоновки данных параметр должен быть объявлен с типом Массив и источником значений Выбор из справочника (если это массив ссылок). Для массивов значений используйте тип Строка с разделителем (например, запятая) и последующим разбором.

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

8. Частые вопросы и решения

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

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

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

МассивДат.Добавить(НачалоДня(ТекущаяДата()));

МассивДат.Добавить(КонецДня(ТекущаяДата() - 30));

Запрос.Текст = "ВЫБРАТЬ * ИЗ Документ.Заказ ГДЕ Дата В(&Даты)";

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

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

Основные причины:

  1. Отсутствие индексов на полях, по которым идёт фильтрация массивом.
  2. Слишком большой массив (>1000 элементов без временной таблицы).
  3. Использование ПОДОБИЕ или НАЙТИ вместо точного сравнения.

Решение: проверьте план выполнения запроса в консоли запросов (Отладка → План запроса).

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

Нет, напрямую передать массив объектов нельзя. Нужно:

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

Пример для массива документов:

МассивСсылок = МассивДокументов.ВыгрузитьКолонку("Ссылка");

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

Как отлаживать ошибки с массивами в запросах?

Используйте эти приёмы:

  • Проверяйте тип элементов массива: Сообщить(ТипЗнч(Массив[0])).
  • Для больших массивов тестируйте на подмножестве (первые 10 элементов).
  • Включите трассировку SQL-запросов в файле 1CV8.lg (параметр запуска /L-).
Есть ли разница между В(&Массив) и = ЗНАЧЕНИЕ(&Массив)?

Да, это принципиально разные конструкции:

  • В(&Массив) — проверяет вхождение значения в массив (аналог SQL IN).
  • = ЗНАЧЕНИЕ(&Массив) — сравнивает значение с массивом как с единым объектом (практически никогда не используется).