Работа с массивами в запросах 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, Дата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. Альтернативные подходы: массивы в параметрах отчётов
При разработке отчётов в 1С массивы часто передаются как параметры формы. Здесь важно учитывать, что механизм передачи параметров в схему компоновки данных (СКД) отличается от обычных запросов.
Пример настройки параметра отчёта для приёма массива:
// В модуле объекта отчёта
Процедура ПриКомпоновкеДанных(ДанныеКомпоновки, СтандартнаяОбработка)
Параметры = ДанныеКомпоновки.ПараметрыДанных;
Если Параметры.Свойство("МассивКонтрагентов") Тогда
Массив = Параметры.МассивКонтрагентов.Значение;
Если Массив.Количество() > 0 Тогда
Запрос = ДанныеКомпоновки.Запрос;
Запрос.УстановитьПараметр("Контрагенты", Массив);
КонецЕсли;
КонецЕсли;
КонецПроцедуры
В схеме компоновки данных параметр должен быть объявлен с типом Массив и источником значений Выбор из справочника (если это массив ссылок). Для массивов значений используйте тип Строка с разделителем (например, запятая) и последующим разбором.
Важно: при передаче массива через СКД проверяйте его на пустоту, иначе запрос может вернуть все записи без фильтрации.
8. Частые вопросы и решения
Как передать массив дат в запрос?
Массив дат передаётся так же, как и другие простые типы, но убедитесь, что все элементы имеют тип Дата:
МассивДат = Новый Массив();
МассивДат.Добавить(НачалоДня(ТекущаяДата()));
МассивДат.Добавить(КонецДня(ТекущаяДата() - 30));
Запрос.Текст = "ВЫБРАТЬ * ИЗ Документ.Заказ ГДЕ Дата В(&Даты)";
Запрос.УстановитьПараметр("Даты", МассивДат);
Почему запрос с массивом работает медленно?
Основные причины:
- Отсутствие индексов на полях, по которым идёт фильтрация массивом.
- Слишком большой массив (>1000 элементов без временной таблицы).
- Использование
ПОДОБИЕилиНАЙТИвместо точного сравнения.
Решение: проверьте план выполнения запроса в консоли запросов (Отладка → План запроса).
Можно ли передать массив объектов (например, документов) в запрос?
Нет, напрямую передать массив объектов нельзя. Нужно:
- Выгрузить из объектов нужные поля (например, ссылки или идентификаторы).
- Передать их как массив простых типов или через временную таблицу.
Пример для массива документов:
МассивСсылок = МассивДокументов.ВыгрузитьКолонку("Ссылка");
Запрос.УстановитьПараметр("Документы", МассивСсылок);
Как отлаживать ошибки с массивами в запросах?
Используйте эти приёмы:
- Проверяйте тип элементов массива:
Сообщить(ТипЗнч(Массив[0])). - Для больших массивов тестируйте на подмножестве (первые 10 элементов).
- Включите трассировку SQL-запросов в файле
1CV8.lg(параметр запуска/L-).
Есть ли разница между В(&Массив) и = ЗНАЧЕНИЕ(&Массив)?
Да, это принципиально разные конструкции:
В(&Массив)— проверяет вхождение значения в массив (аналог SQLIN).= ЗНАЧЕНИЕ(&Массив)— сравнивает значение с массивом как с единым объектом (практически никогда не используется).