Работа с перечислениями в запросах 1С:Предприятие — одна из самых востребованных задач при разработке отчетов, обработок и сложной бизнес-логики. Независимо от того, нужно ли отфильтровать документы по нескольким типам, выбрать данные из справочников с определенными кодами или сгруппировать записи по набору значений, умение корректно задавать перечисления сэкономит часы отладки и оптимизирует производительность.
В этой статье мы разберем все актуальные способы задания перечислений в запросах 1С 8.3 — от базового синтаксиса с оператором IN до продвинутых техник с временными таблицами и динамическими параметрами. Особое внимание уделим типичным ошибкам, которые приводят к неверным результатам или падению производительности, а также нюансам работы с разными типами данных (строки, числа, даты, ссылки).
Материал будет полезен как начинающим разработчикам, которые только осваивают язык запросов, так и опытным специалистам, ищущим оптимальные решения для сложных выборок. Все примеры протестированы на актуальных релизах платформы 1С:Предприятие 8.3.23 и выше.
1. Базовый синтаксис: оператор IN для статических перечислений
Самый простой и распространенный способ задать перечисление в запросе — использовать оператор IN. Он позволяет указать набор значений, с которыми будет сравниваться поле таблицы. Синтаксис интуитивно понятен:
ВЫБРАТЬ
Номенклатура.Наименование,
Номенклатура.Артикул
ИЗ
Справочник.Номенклатура КАК Номенклатура
ГДЕ
Номенклатура.ВидНоменклатуры В (&ВидТовара, &ВидУслуги, &ВидКомплекта)
В этом примере мы отбираем номенклатуру, вид которой совпадает с одним из трех переданных значений. Обратите внимание, что значения в скобках разделяются запятыми, а сами скобки обязательны.
- ✅ Преимущества: простой и наглядный синтаксис, высокая читаемость кода.
- ⚠️ Ограничения: не подходит для динамических списков (придется переписывать запрос) и больших перечислений (более 1000 элементов может привести к ошибке компиляции).
- 🔄 Альтернатива: для длинных списков лучше использовать временные таблицы (разберём ниже).
Важный нюанс: оператор IN работает с любыми типами данных, но для ссылочных типов (справочники, документы) значения должны быть одного типа. Например, нельзя в одном перечислении смешивать ссылки на Справочник.Номенклатура и Справочник.Контрагенты — это приведет к ошибке выполнения.
2. Оператор OR: когда IN не подходит
В некоторых случаях вместо IN удобнее использовать цепочку условий с оператором OR. Это актуально, когда:
- 🔍 Нужно комбинировать разные типы условий (например, проверять и вид номенклатуры, и её группу).
- 📊 Условия отбора сложнее простого равенства (например, "наименование начинается с...").
- 🔄 Требуется гибкость в формировании логики (например, часть условий добавляется динамически).
Пример использования OR для перечисления:
ВЫБРАТЬ
Документ.Дату,
Документ.Номер,
Документ.СуммаДокумента
ИЗ
Документ.РеализацияТоваровУслуг КАК Документ
ГДЕ
Документ.Организация = &ТекущаяОрганизация
И (
Документ.ВидОперации = ЗНАЧЕНИЕ(Перечисление.ВидыОперацийРеализации.Продажа)
ИЛИ Документ.ВидОперации = ЗНАЧЕНИЕ(Перечисление.ВидыОперацийРеализации.Комиссия)
ИЛИ Документ.ВидОперации = ЗНАЧЕНИЕ(Перечисление.ВидыОперацийРеализации.Экспорт)
)
Ключевое отличие от IN: здесь каждое условие пишется явно, что позволяет добавлять дополнительные проверки. Например, можно комбинировать:
ГДЕ
(Документ.Контрагент = &Поставщик1 И Документ.СуммаДокумента > 10000)
ИЛИ (Документ.Контрагент = &Поставщик2 И Документ.Дату > &ДатаНачала)
⚠️ Внимание: При использованииORс большим количеством условий (более 5-7) запрос может стать менее производительным, чем эквивалентный вариант сIN. Это связано с тем, как 1С оптимизирует план выполнения.
3. Динамические перечисления: параметры и массивы
Статические перечисления удобны, но часто значения для фильтрации приходят извне — например, пользователь выбирает элементы в форме или данные загружаются из файла. В таких случаях используют параметры запроса или временные таблицы.
Пример с параметром-массивом:
// В коде перед выполнением запроса:
МассивВидов = Новый Массив;
МассивВидов.Добавить(Перечисление.ВидыНоменклатуры.Товар);
МассивВидов.Добавить(Перечисление.ВидыНоменклатуры.Услуга);
// В тексте запроса:
ВЫБРАТЬ
Номенклатура.Наименование
ИЗ
Справочник.Номенклатура КАК Номенклатура
ГДЕ
Номенклатура.ВидНоменклатуры В (&МассивВидов)
Важные моменты:
- 📌 Параметр
&МассивВидовдолжен быть объявлен в запросе какТип: Массив. - 🔄 Если массив пустой, условие
В (&МассивВидов)вернет все записи (это распространенная ошибка!). Чтобы избежать этого, добавьте проверку:ГДЕ&МассивВидов.Количество() > 0
И Номенклатура.ВидНоменклатуры В (&МассивВидов)
- 🚀 Для больших массивов (более 100 элементов) лучше использовать временные таблицы — это ускорит выполнение.
Массив не пустой|Все элементы массива одного типа|Параметр объявлен в запросе с правильным типом|Добавлена защита от пустого массива-->
4. Временные таблицы для сложных перечислений
Когда перечисление содержит сотни или тысячи значений, передача их через параметр IN становится неэффективной. В таких случаях оптимальное решение — загрузить значения во временную таблицу и присоединить её к основному запросу.
Пример с использованием временной таблицы для фильтрации по большому списку контрагентов:
// 1. Создаем временную таблицу с нужными контрагентами
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| КонтрагентыСписок.Ссылка КАК Контрагент
|ИЗ
| &ТаблицаКонтрагентов КАК КонтрагентыСписок";
Запрос.УстановитьПараметр("ТаблицаКонтрагентов", ТаблицаКонтрагентов); // ТаблицаЗначений с колонкой "Ссылка"
// 2. Основной запрос с присоединением временной таблицы
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Документ.Дату,
| Документ.Номер,
| Документ.Контрагент.Наименование
|ИЗ
| Документ.ЗаказПокупателя КАК Документ
| ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТКонтрагенты КАК ФильтрКонтрагентов
| ПО Документ.Контрагент = ФильтрКонтрагентов.Контрагент";
Запрос.УстановитьВременнуюТаблицу("ВТКонтрагенты", РезультатПервогоЗапроса);
Преимущества этого подхода:
| Критерий | Оператор IN | Временная таблица |
|---|---|---|
| Производительность при 1000+ значений | Низкая (запрос может "зависнуть") | Высокая (оптимизировано СУБД) |
| Гибкость (дополнительные поля) | Ограничена (только сравнение) | Высокая (можно добавлять условия) |
| Читаемость кода | Высокая для простых случаев | Средняя (требует двух запросов) |
⚠️ Внимание: При использовании временных таблиц в 1С:Предприятие 8.3 важно следить за транзакциями. Если временная таблица создается в одной транзакции, а используется в другой, это приведет к ошибке. Все операции с временными таблицами должны выполняться в рамках одной транзакции.
5. Перечисления в конструкторе запросов: визуальное создание
Для пользователей, не владеющих языком запросов, удобно использовать конструктор запросов в конфигураторе. Он позволяет визуально строить условия, включая перечисления, без ручного ввода кода.
Пошаговая инструкция:
- Откройте конструктор запросов (
Файл → Новый → Запросили через контекстное меню объекта). - Добавьте нужные таблицы (например, Документ.РеализацияТоваровУслуг).
- В разделе
Условиявыберите поле для фильтрации (например,ВидОперации). - В выпадающем списке операторов выберите
В списке(эквивалентIN). - Нажмите на кнопку
...рядом с полем значения и выберите нужные элементы из справочника или перечисления. - Сгенерируйте текст запроса и при необходимости отредактируйте его вручную.
Пример сгенерированного конструктором кода для перечисления видов операций:
ВЫБРАТЬ
РеализацияТоваровУслуг.Ссылка КАК Ссылка,
РеализацияТоваровУслуг.Номер КАК Номер
ИЗ
Документ.РеализацияТоваровУслуг КАК РеализацияТоваровУслуг
ГДЕ
РеализацияТоваровУслуг.ВидОперации В (
ЗНАЧЕНИЕ(Перечисление.ВидыОперацийРеализации.Продажа),
ЗНАЧЕНИЕ(Перечисление.ВидыОперацийРеализации.Комиссия)
)
Если в конструкторе не отображаются нужные значения для перечисления, проверьте права доступа текущего пользователя к справочникам и перечислениям в режиме 1С:Предприятие.
6. Типичные ошибки и как их избежать
Даже опытные разработчики иногда допускают ошибки при работе с перечислениями в запросах. Рассмотрим самые распространенные из них и способы их решения.
Ошибка 1: Смешение типов данных в перечислении
Если в одном перечислении передать значения разных типов (например, строку и число), 1С выдаст ошибку:
ГДЕ
Номенклатура.Артикул В ("А001", 12345) // Ошибка: нельзя сравнивать строку и число
Решение: Приведите все значения к одному типу с помощью функций СТРОКА() или ЧИСЛО():
ГДЕ
Номенклатура.Артикул В (СТРОКА(12345), "А001")
Ошибка 2: Пустое перечисление возвращает все записи
Как упоминалось ранее, условие В (&Массив) с пустым массивом вернет все строки таблицы. Это может привести к неожиданным результатам.
Решение: Всегда добавляйте проверку на пустоту:
ГДЕ
&Массив.Количество() > 0
И Поле В (&Массив)
Ошибка 3: Использование IN с подзапросами
Некоторые разработчики пытаются использовать IN с подзапросами, что приводит к синтаксическим ошибкам:
ГДЕ
Документ.Контрагент В (
ВЫБРАТЬ Контрагенты.Ссылка ИЗ Справочник.Контрагенты КАК Контрагенты
) // Так нельзя!
Решение: Используйте оператор ВЫРАЗИТЬ или СУЩЕСТВУЕТ:
ГДЕ
СУЩЕСТВУЕТ (
ВЫБРАТЬ NULL ИЗ Справочник.Контрагенты КАК Контрагенты
ГДЕ Контрагенты.Ссылка = Документ.Контрагент
)
Почему нельзя использовать IN с подзапросами?
В языке запросов 1С оператор IN предназначен только для статических списков значений или параметров. Подзапрос возвращает динамический набор данных, который нельзя напрямую подставить в IN. Для таких случаев предназначены операторы СУЩЕСТВУЕТ или ВЫРАЗИТЬ, которые корректно обрабатывают динамические наборы.
Ошибка 4: Перечисления с NULL-значениями
Если в перечислении может присутствовать NULL (например, при фильтрации по необязательному реквизиту), стандартный IN не сработает:
ГДЕ
Документ.Ответственный В (NULL, &Менеджер1, &Менеджер2) // NULL будет проигнорирован
Решение: Используйте явную проверку на ЕСТЬ NULL:
ГДЕ
Документ.Ответственный ЕСТЬ NULL
ИЛИ Документ.Ответственный В (&Менеджер1, &Менеджер2)
⚠️ Внимание: В некоторых версиях платформы 1С:Предприятие 8.3 (до 8.3.18) были баги с обработкой NULL в перечислениях. Если вы работаете со старыми релизами, тестируйте такие запросы особенно тщательно.
7. Оптимизация запросов с перечислениями
Перечисления могут значительно влиять на производительность запросов, особенно если они используются в больших базах данных. Вот ключевые рекомендации по оптимизации:
- 🚀 Индексы: Убедитесь, что поля, по которым идет фильтрация через
IN, проиндексированы. Например, для поляКонтрагентв документах должен быть индекс. - 📊 Размер перечисления: Если в
INпередается более 100 значений, рассмотрите вариант с временной таблицей. - 🔄 Порядок условий: Размещайте условия с перечислениями как можно ближе к началу секции
ГДЕ, чтобы СУБД могла раньше отфильтровать данные. - 🛠 Анализ плана: Используйте
ОбъяснитьЗапрос()для проверки, как 1С оптимизирует ваш запрос с перечислениями.
Пример оптимизированного запроса с учетом этих рекомендаций:
ВЫБРАТЬ ПЕРВЫЕ 100
Документ.Дату,
Документ.Номер,
Документ.СуммаДокумента
ИЗ
Документ.ПоступлениеТоваровУслуг КАК Документ
ГДЕ
Документ.Организация = &ТекущаяОрганизация
И Документ.Дату МЕЖДУ &ДатуНачала И &ДатуОкончания
И Документ.Контрагент В (&СписокКонтрагентов) // Перечисление ближе к началу
УПОРЯДОЧИТЬ ПО
Документ.Дату УБЫВ
Критическая ошибка: в запросах с большими перечислениями (более 1000 элементов) никогда не используйте конструкцию НЕ В (&Список) — это приводит к полному сканированию таблицы и падению производительности. Вместо этого используйте ВНУТРЕННЕЕ СОЕДИНЕНИЕ с временной таблицей исключаемых значений.
8. Продвинутые техники: перечисления в объединениях и подзапросах
В сложных запросах перечисления могут использоваться не только в секции ГДЕ, но и в других конструкциях. Рассмотрим продвинутые сценарии.
Перечисления в секции ИМЕЮЩИЕ
Если нужно отфильтровать сгруппированные данные по набору значений, используйте ИМЕЮЩИЕ с IN:
ВЫБРАТЬ
Контрагент.Наименование,
СУММА(Документ.СуммаДокумента) КАК ОбщаяСумма
ИЗ
Документ.РеализацияТоваровУслуг КАК Документ
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.Контрагенты КАК Контрагент
ПО Документ.Контрагент = Контрагент.Ссылка
ГДЕ
Документ.Дату > &ДатаНачала
ГРУППИРОВАТЬ ПО
Контрагент.Наименование
ИМЕЮЩИЕ
Контрагент.Наименование В (&СписокКрупныхКонтрагентов)
Перечисления в подзапросах с ВЫРАЗИТЬ
Для динамического формирования списка значений прямо в запросе используйте ВЫРАЗИТЬ:
ВЫБРАТЬ
Номенклатура.Наименование
ИЗ
Справочник.Номенклатура КАК Номенклатура
ГДЕ
Номенклатура.ВидНоменклатуры В (
ВЫРАЗИТЬ(
ВЫБРАТЬ РАЗЛИЧНЫЕ Вид.Ссылка ИЗ Справочник.ВидыНоменклатуры КАК Вид
ГДЕ Вид.ЭтоГруппа = ЛОЖЬ
КАК СписокВидов)
)
Перечисления в объединениях (ОБЪЕДИНИТЬ)
При объединении нескольких выборок можно фильтровать каждую часть отдельно:
ВЫБРАТЬ
Документ.Номер КАК Документ,
"Продажа" КАК ТипОперации
ИЗ
Документ.РеализацияТоваровУслуг КАК Документ
ГДЕ
Документ.ВидОперации = ЗНАЧЕНИЕ(Перечисление.ВидыОперацийРеализации.Продажа)
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
Документ.Номер КАК Документ,
"Комиссия" КАК ТипОперации
ИЗ
Документ.РеализацияТоваровУслуг КАК Документ
ГДЕ
Документ.ВидОперации = ЗНАЧЕНИЕ(Перечисление.ВидыОперацийРеализации.Комиссия)
При использовании перечислений в объединениях (ОБЪЕДИНИТЬ) фильтрацию лучше применять к каждой части отдельно — это ускорит выполнение запроса за счет раннего отсечения ненужных данных.
FAQ: Частые вопросы по перечислениям в запросах 1С
Можно ли в одном перечислении использовать значения разных типов, например строки и числа?
Нет, это приведет к ошибке выполнения запроса. Все значения в перечислении должны быть одного типа. Если нужно сравнивать разные типы, приведите их к одному с помощью функций СТРОКА(), ЧИСЛО() или ДАТАВРЕМЯ().
Как передать в запрос перечисление из 10 000 элементов?
Для больших перечислений (более 1000 элементов) не рекомендуется использовать оператор IN — это приведет к падению производительности. Оптимальное решение:
- Загрузите значения во временную таблицу.
- Присоедините её к основному запросу через
ВНУТРЕННЕЕ СОЕДИНЕНИЕ.
Пример кода смотрите в разделе "Временные таблицы для сложных перечислений".
Почему запрос с перечислением работает медленно?
Основные причины:
- Отсутствует индекс на поле, по которому идет фильтрация.
- Перечисление содержит слишком много элементов (более 1000).
- Условие с
INрасположено в конце секцииГДЕ, из-за чего СУБД не может оптимизировать план выполнения. - В перечислении используются сложные выражения (например, вычисления над полями).
Решения:
- Добавьте индексы на фильтруемые поля.
- Для больших перечислений используйте временные таблицы.
- Перенесите условие с
INближе к началу секцииГДЕ. - Проанализируйте план запроса с помощью
ОбъяснитьЗапрос().
Как в конструкторе запросов задать перечисление для поля типа "Справочник"?
В конструкторе:
- Добавьте таблицу с нужным справочником.
- В разделе
Условиявыберите поле справочника. - В операторе выберите
В списке. - Нажмите на кнопку
...рядом с полем значения и выберите элементы из справочника.
Если нужные элементы не отображаются в списке, проверьте:
- Права доступа к справочнику.
- Отборы, установленные в форме справочника.
- Тип поля (например, если поле имеет тип
СправочникСсылка.Контрагенты, нельзя выбирать элементы изСправочник.Номенклатура).
Что делать, если при выполнении запроса с перечислением возникает ошибка "Типы не совпадают"?
Эта ошибка означает, что:
- В перечислении смешаны значения разных типов (например, строка и число).
- Тип параметра запроса не соответствует типу поля (например, параметр объявлен как
Строка, а поле имеет типЧисло). - Для ссылочных типов переданы значения из разных справочников (например, в одном перечислении ссылки на Контрагентов и Номенклатуру).
Решения:
- Проверьте типы всех значений в перечислении с помощью
ТипЗнч(). - Убедитесь, что параметр запроса имеет правильный тип (например,
Тип: СправочникСсылка.Контрагенты). - Для ссылочных типов используйте явное приведение типов с помощью
ЗНАЧЕНИЕ().