Работа с массивами в запросах 1С:Предприятие — одна из самых востребованных, но при этом недостаточно документированных тем среди разработчиков. Многие привыкли оперировать одиночными значениями или временными таблицами, тогда как массивы позволяют радикально упростить код, сократить количество запросов к базе и повысить производительность. Однако их неправильное использование часто приводит к ошибкам выполнения, неоптимальным планам запросов или даже падению сервера при работе с большими объемами данных.
В этой статье мы разберём не только базовый синтаксис работы с массивами в запросах 1С 8.3, но и нюансы, которые редко упоминаются в официальной документации. Вы узнаете, как передавать массивы в параметры запроса, обрабатывать их результаты, избегать типичных ошибок при работе с МАССИВ() и В(), а также оптимизировать сложные конструкции для ускорения выполнения. Особое внимание уделим практическим примерам — от простых фильтров до динамического формирования условий на основе массивов данных.
Что такое массивы в контексте запросов 1С
В языке запросов 1С массив — это не самостоятельный тип данных, а способ передачи набора значений в условие отбора или в качестве параметра. В отличие от встроенного типа Массив в 1С:Предприятие, который представляет собой коллекцию элементов, в запросах массивы используются как:
- 📌 Списки значений для фильтрации (аналог оператора
INв SQL) - 🔄 Наборы параметров для динамического формирования условий
- 📊 Источники данных для временных таблиц (через конструктор
ЗНАЧЕНИЕ())
Важно понимать, что массив в запросе — это не переменная, а синтаксическая конструкция, которая преобразуется платформой в оптимизированный SQL-код. Например, условие ГДЕ Номенклатура В (&МассивТоваров) при выполнении на сервере Microsoft SQL Server будет преобразовано в WHERE Номенклатура IN (@p1, @p2, @p3), где @p1, @p2, @p3 — параметры запроса.
Основные операторы для работы с массивами в запросах:
| Оператор | Описание | Пример использования |
|---|---|---|
В() |
Проверяет вхождение значения в массив (аналог IN) |
ГДЕ Склад В (&СписокСкладов) |
МАССИВ() |
Создаёт массив прямо в тексте запроса | ГДЕ Статус В МАССИВ("Новый", "ВРаботе") |
ЗНАЧЕНИЕ() |
Преобразует массив в таблицу значений для JOIN | ЛЕВОЕ СОЕДИНЕНИЕ ЗНАЧЕНИЕ(&МассивДанных) КАК Т... |
⚠️ Внимание: В ранних версиях платформы (до 1С:Предприятие 8.3.10) оператор МАССИВ() мог работать нестабильно при передаче более 1000 элементов. В современных релизах это ограничение снято, но для больших массивов (>10 000 элементов) рекомендуется использовать временные таблицы.
Как передавать массивы в параметры запроса
Самый распространённый способ работы с массивами — передача их в качестве параметров запроса. Это позволяет динамически формировать условия отбора без изменения текста запроса. Рассмотрим базовый синтаксис:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Товары.Наименование,
| Товары.Артикул
|ИЗ
| Справочник.Номенклатура КАК Товары
|ГДЕ
| Товары.ВидыНоменклатуры В (&ВидыНоменклатуры)";
Запрос.УстановитьПараметр("ВидыНоменклатуры", МассивВидов); // МассивВидов - массив значений
Результат = Запрос.Выполнить();
Ключевые моменты:
- 🔹 Параметр в тексте запроса обозначается как
&ИмяПараметра - 🔹 Массив передаётся через метод
УстановитьПараметр()до выполнения запроса - 🔹 Тип элементов массива должен соответствовать типу поля в базе (например, нельзя передать массив строк в параметр для поля типа
Число)
Если массив пустой, условие В (&Параметр) вернёт пустой результат, так как ни одно значение не удовлетворит условию. Чтобы избежать этого, используйте конструкцию:
Запрос.Текст =
"ВЫБРАТЬ
| Товары.Наименование
|ИЗ
| Справочник.Номенклатура КАК Товары
|ГДЕ
| " + (?(МассивВидов.Количество() = 0, "ИСТИНА", "Товары.ВидыНоменклатуры В (&ВидыНоменклатуры)") + "";
Создание массивов прямо в тексте запроса (МАССИВ)
Оператор МАССИВ() позволяет создавать массивы непосредственно в тексте запроса без передачи параметров. Это удобно для статических условий или небольших наборов значений. Синтаксис:
Запрос.Текст =
"ВЫБРАТЬ
| Документы.Номер,
| Документы.Дата
|ИЗ
| Документ.ЗаказПокупателя КАК Документы
|ГДЕ
| Документы.Статус В МАССИВ("Новый", "НаСогласовании", "Отменен")";
Особенности использования МАССИВ():
- 📝 Элементы массива перечисляются через запятую в круглых скобках
- 📌 Поддерживаются все базовые типы: строки, числа, даты, булевы значения
- 🚫 Нельзя использовать переменные или выражения внутри
МАССИВ()— только литералы
Пример с разными типами данных:
Запрос.Текст =
"ВЫБРАТЬ
| Клиенты.Наименование
|ИЗ
| Справочник.Контрагенты КАК Клиенты
|ГДЕ
| Клиенты.Вид В МАССИВ("ЮридическоеЛицо", "ИП")
| И Клиенты.ДатаРегистрации > МАССИВ(ДАТАВРЕМЯ(2020,1,1))[0]";
⚠️ Внимание: При использованииМАССИВ()с датами обязательно указывайте элементы какДАТАВРЕМЯ()илиДАТА(). Передача строковых дат (например,"01.01.2020") приведёт к ошибке преобразования типов.
Элементы массива статичны (не требуют динамического формирования)|
Количество элементов не превышает 100 (для больших массивов лучше использовать параметры)|
Типы данных всех элементов совпадают с типом поля в условии|
Нет необходимости в повторном использовании массива в других частях запроса-->
Работа с массивами в конструкции ВЫРАЗИТЬ
Оператор ВЫРАЗИТЬ позволяет преобразовывать массивы в строковые представления или наоборот — строки в массивы. Это полезно для динамического формирования условий или работы с данными в нестандартных форматах. Рассмотрим пример:
Запрос.Текст =
"ВЫБРАТЬ
| ВЫРАЗИТЬ(МАССИВ("А", "Б", "В") КАК СТРОКА) КАК СтрокаИзМассива,
| ВЫРАЗИТЬ("1,2,3" КАК МАССИВ(ЧИСЛО)) КАК МассивИзСтроки";
Результат выполнения этого запроса:
- 📌 Первое поле вернёт строку:
"А,Б,В" - 📌 Второе поле преобразует строку
"1,2,3"в массив чисел:[1, 2, 3]
Практическое применение ВЫРАЗИТЬ с массивами:
| Задача | Пример кода | Результат |
|---|---|---|
| Преобразование массива в строку для отладки | ВЫРАЗИТЬ(&Массив КАК СТРОКА) |
"элемент1,элемент2,элемент3" |
| Разбор строки с разделителями в массив | ВЫРАЗИТЬ("a;b;c" КАК МАССИВ(СТРОКА), ";") |
["a", "b", "c"] |
| Фильтрация по массиву, хранящемуся в строковом поле | ГДЕ ВЫРАЗИТЬ(Документ.Теги КАК МАССИВ) СОДЕРЖИТ "Срочно" |
Документы, в поле Теги которых есть слово "Срочно" |
Важно: При преобразовании строки в массив с помощью ВЫРАЗИТЬ(... КАК МАССИВ) можно указать разделитель (по умолчанию — запятая). Например:
ВЫРАЗИТЬ("яблоко|груша|банан" КАК МАССИВ(СТРОКА), "|")
Запрос.УстановитьПараметр("СтрокаТегов", "тег1,тег2,тег3");
Запрос.Текст = "ГДЕ ВЫРАЗИТЬ(&СтрокаТегов КАК МАССИВ) СОДЕРЖИТ Тег";
-->
Оптимизация запросов с массивами: типичные ошибки и решения
Неправильное использование массивов в запросах может привести к значительному падению производительности. Рассмотрим наиболее распространённые ошибки и способы их исправления:
1. Передача больших массивов в параметры
Если массив содержит тысячи элементов, его передача в параметр запроса может вызвать:
- 🐢 Замедление выполнения из-за генерации длинного SQL-кода
- 💥 Переполнение буфера параметров (особенно в PostgreSQL)
Решение: Для массивов размером >1000 элементов используйте временные таблицы:
// 1. Создаём временную таблицу
ВТ = Новый ТаблицаЗначений;
ВТ.Колонки.Добавить("Значение");
// 2. Заполняем данными из массива
Для Каждого Элемент Из БольшойМассив Цикл
Строка = ВТ.Добавить();
Строка.Значение = Элемент;
КонецЦикла;
// 3. Используем во временной таблице
Запрос.Текст =
"ВЫБРАТЬ
| Товары.Наименование
|ИЗ
| Справочник.Номенклатура КАК Товары
|ГДЕ
| Товары.Код В (ВЫБРАТЬ ВТ.Значение ИЗ &ВТ КАК ВТ)";
2. Несоответствие типов данных
Ошибка "Тип не совпадает с ожидаемым" возникает, когда:
- 🔢 В массив чисел передаются строки (например,
["1", "2"]вместо[1, 2]) - 📅 В массив дат передаются строки без преобразования
Решение: Всегда проверяйте типы элементов перед передачей:
Если ТипЗнч(Массив[0]) <> Тип("Число") Тогда
Для Инд = 0 По Массив.ВГраница() Цикл
Массив[Инд] = Число(Массив[Инд]); // Преобразуем в число
КонецЦикла;
КонецЕсли;
3. Избыточные условия с массивами
Частая ошибка — использование В () там, где достаточно простого сравнения:
// Некорректно (если массив всегда содержит 1 элемент)
ГДЕ Статус В (&МассивСтатусов)
// Корректно
ГДЕ Статус = &Статус
Для массивов размером 1-3 элемента оператор В() работает эффективнее, чем несколько условий с ИЛИ. Но для больших массивов (>10 элементов) временные таблицы дают лучшую производительность.
Практические примеры использования массивов
Разберём реальные сценарии, где массивы в запросах 1С позволяют решить задачи элегантно и эффективно.
Пример 1: Фильтрация по динамическому списку значений
Задача: Выбрать документы, у которых статус входит в список, формируемый пользователем.
Процедура ПолучитьДокументыПоСтатусам(СписокСтатусов)
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Документы.Ссылка КАК Ссылка,
| Документы.Дата
|ИЗ
| Документ.ЗаказПокупателя КАК Документы
|ГДЕ
| Документы.Статус В (&Статусы)";
Запрос.УстановитьПараметр("Статусы", СписокСтатусов);
Возврат Запрос.Выполнить();
КонецПроцедуры
Пример 2: Поиск по нескольким полям с использованием массива
Задача: Найти контрагентов, у которых хотя бы одно из полей (телефон, email, сайт) содержит искомое значение.
Запрос.Текст =
"ВЫБРАТЬ
| Контрагенты.Наименование
|ИЗ
| Справочник.Контрагенты КАК Контрагенты
|ГДЕ
| (Контрагенты.Телефон ПОДОБНО &ПоисковыйЗапрос)
| ИЛИ (Контрагенты.Email ПОДОБНО &ПоисковыйЗапрос)
| ИЛИ (Контрагенты.Сайт ПОДОБНО &ПоисковыйЗапрос)";
Запрос.УстановитьПараметр("ПоисковыйЗапрос", "%" + Поиск + "%");
Альтернативный вариант с массивом полей:
Запрос.Текст =
"ВЫБРАТЬ
| Контрагенты.Наименование
|ИЗ
| Справочник.Контрагенты КАК Контрагенты
|ГДЕ
| ВЫРАЗИТЬ(Контрагенты.Телефон КАК СТРОКА) ПОДОБНО &ПоисковыйЗапрос
| ИЛИ ВЫРАЗИТЬ(Контрагенты.Email КАК СТРОКА) ПОДОБНО &ПоисковыйЗапрос
| ИЛИ ВЫРАЗИТЬ(Контрагенты.Сайт КАК СТРОКА) ПОДОБНО &ПоисковыйЗапрос";
Пример 3: Группировка по значениям из массива
Задача: Получить остатки товаров только по заданным складам.
Запрос.Текст =
"ВЫБРАТЬ
| ОстаткиТоваров.Номенклатура КАК Номенклатура,
| СУММА(ОстаткиТоваров.Количество) КАК Количество
|ИЗ
| РегистрНакопления.ОстаткиТоваров КАК ОстаткиТоваров
|ГДЕ
| ОстаткиТоваров.Склад В (&СписокСкладов)
|СГРУППИРОВАТЬ ПО
| ОстаткиТоваров.Номенклатура";
Как ускорить запрос с большим массивом складов?
Если список складов содержит >100 элементов, замените условие В (&СписокСкладов) на соединение с временной таблицей:
ВТ = Новый ТаблицаЗначений;
ВТ.Колонки.Добавить("Склад");
// Заполняем ВТ данными из массива
Запрос.Текст =
"ВЫБРАТЬ
| Остатки.Номенклатура,
| СУММА(Остатки.Количество) КАК Количество
|ИЗ
| РегистрНакопления.ОстаткиТоваров КАК Остатки
| ЛЕВОЕ СОЕДИНЕНИЕ &ВТ КАК ВТ
| ПО Остатки.Склад = ВТ.Склад
|СГРУППИРОВАТЬ ПО Остатки.Номенклатура";
Это снизит нагрузку на СУБД за счёт использования индексов.
Особенности работы с массивами в разных СУБД
Платформа 1С:Предприятие поддерживает несколько типов СУБД, и поведение запросов с массивами может отличаться:
| СУБД | Особенности работы с массивами | Рекомендации |
|---|---|---|
| Microsoft SQL Server | Поддерживает параметры массивов размером до 2100 элементов. При превышении — ошибка. | Для больших массивов используйте временные таблицы или разбивайте запрос. |
| PostgreSQL | Ограничение на размер параметра — ~10 000 элементов. Медленнее обрабатывает большие массивы. | Оптимальный размер массива — до 1000 элементов. Для больших данных используйте UNNEST(). |
| IBM DB2 | Хорошо оптимизирует запросы с массивами, но может выдавать ошибки при несоответствии типов. | Всегда явно преобразуйте типы (например, ЧИСЛО() для числовых массивов). |
| Файловая база | Массивы размером >100 элементов значительно замедляют выполнение. | Избегайте больших массивов. Для фильтрации используйте циклы по элементам. |
Пример оптимизации для PostgreSQL:
// Вместо:
ГДЕ Товар В (&БольшойМассивТоваров)
// Используйте:
ГДЕ Товар В (
ВЫБРАТЬ
Элемент КАК Товар
ИЗ
&БольшойМассивТоваров КАК Элемент
)
⚠️ Внимание: В файловом варианте 1С запросы с массивами размером >500 элементов могут приводить к ошибке "Недостаточно памяти". В этом случае разбивайте массив на части и выполняйте несколько запросов с последующим объединением результатов.
FAQ: Частые вопросы по работе с массивами в запросах
Можно ли использовать массивы в подзапросах?
Да, массивы можно передавать в подзапросы через параметры. Например:
Запрос.Текст =
"ВЫБРАТЬ
| Товары.Наименование
|ИЗ
| Справочник.Номенклатура КАК Товары
|ГДЕ
| Товары.Код В (
| ВЫБРАТЬ Коды.Значение ИЗ &Коды КАК Коды
| )";
Где &Коды — параметр, содержащий массив кодов номенклатуры.
Как передать массив структур в запрос?
Прямая передача массива структур в запрос невозможна. Необходимо:
- Преобразовать массив структур в
ТаблицаЗначений - Использовать её как временную таблицу в запросе
ТЗ = Новый ТаблицаЗначений;
ТЗ.Колонки.Добавить("Поле1");
ТЗ.Колонки.Добавить("Поле2");
// Заполняем ТЗ данными из массива структур
Запрос.Текст = "ВЫБРАТЬ ... ЛЕВОЕ СОЕДИНЕНИЕ &ТЗ КАК ВТ ...";
Почему запрос с массивом работает медленно?
Основные причины:
- 🔍 Отсутствует индекс по полю, используемому в условии
В () - 📦 Слишком большой массив (>1000 элементов) передаётся в параметр
- 🔄 Неоптимальный план выполнения (проверьте в консоли запросов)
Решения:
- Добавьте индекс по фильтруемому полю
- Для больших массивов используйте временные таблицы
- Проверьте план выполнения и при необходимости используйте подсказки оптимизатору
Можно ли в массиве использовать NULL-значения?
Да, но с оговорками:
- 🔹 В
МАССИВ()нельзя явно указатьNULL— вместо этого используйтеЗНАЧЕНИЕ(NULL) - 🔹 При передаче массива в параметр
NULL-значения сохранятся
Пример:
Запрос.Текст = "ВЫБРАТЬ ... ГДЕ Поле В МАССИВ(1, ЗНАЧЕНИЕ(NULL), 3)";
Как сравнить два массива в запросе?
Прямого оператора для сравнения массивов в языке запросов 1С нет. Альтернативные подходы:
- Через строковое представление:
ГДЕ ВЫРАЗИТЬ(&Массив1 КАК СТРОКА) = ВЫРАЗИТЬ(&Массив2 КАК СТРОКА)Минус: Чувствителен к порядку элементов.
- Через проверку вхождения:
ГДЕ НЕ СУЩЕСТВУЕТ (ВЫБРАТЬ Элемент ИЗ &Массив1 КАК Элемент
ГДЕ НЕ Элемент В (&Массив2)
)
И НЕ СУЩЕСТВУЕТ (
ВЫБРАТЬ Элемент ИЗ &Массив2 КАК Элемент
ГДЕ НЕ Элемент В (&Массив1)
)
Плюс: Работает независимо от порядка элементов.