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

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

Материал будет полезен как начинающим разработчикам, которые только осваивают язык запросов, так и опытным специалистам, ищущим оптимальные решения для сложных выборок. Все примеры протестированы на актуальных релизах платформы 1С:Предприятие 8.3.23 и выше.

1. Базовый синтаксис: оператор IN для статических перечислений

Самый простой и распространенный способ задать перечисление в запросе — использовать оператор IN. Он позволяет указать набор значений, с которыми будет сравниваться поле таблицы. Синтаксис интуитивно понятен:

ВЫБРАТЬ

Номенклатура.Наименование,

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

ИЗ

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

ГДЕ

Номенклатура.ВидНоменклатуры В (&ВидТовара, &ВидУслуги, &ВидКомплекта)

В этом примере мы отбираем номенклатуру, вид которой совпадает с одним из трех переданных значений. Обратите внимание, что значения в скобках разделяются запятыми, а сами скобки обязательны.

  • Преимущества: простой и наглядный синтаксис, высокая читаемость кода.
  • ⚠️ Ограничения: не подходит для динамических списков (придется переписывать запрос) и больших перечислений (более 1000 элементов может привести к ошибке компиляции).
  • 🔄 Альтернатива: для длинных списков лучше использовать временные таблицы (разберём ниже).

Важный нюанс: оператор IN работает с любыми типами данных, но для ссылочных типов (справочники, документы) значения должны быть одного типа. Например, нельзя в одном перечислении смешивать ссылки на Справочник.Номенклатура и Справочник.Контрагенты — это приведет к ошибке выполнения.

📊 Какой оператор для перечислений вы используете чаще?
IN
OR
Временные таблицы
Динамические параметры

2. Оператор OR: когда IN не подходит

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

  • 🔍 Нужно комбинировать разные типы условий (например, проверять и вид номенклатуры, и её группу).
  • 📊 Условия отбора сложнее простого равенства (например, "наименование начинается с...").
  • 🔄 Требуется гибкость в формировании логики (например, часть условий добавляется динамически).

Пример использования OR для перечисления:

ВЫБРАТЬ

Документ.Дату,

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

Документ.СуммаДокумента

ИЗ

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

ГДЕ

Документ.Организация = &ТекущаяОрганизация

И (

Документ.ВидОперации = ЗНАЧЕНИЕ(Перечисление.ВидыОперацийРеализации.Продажа)

ИЛИ Документ.ВидОперации = ЗНАЧЕНИЕ(Перечисление.ВидыОперацийРеализации.Комиссия)

ИЛИ Документ.ВидОперации = ЗНАЧЕНИЕ(Перечисление.ВидыОперацийРеализации.Экспорт)

)

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

ГДЕ

(Документ.Контрагент = &Поставщик1 И Документ.СуммаДокумента > 10000)

ИЛИ (Документ.Контрагент = &Поставщик2 И Документ.Дату > &ДатаНачала)

⚠️ Внимание: При использовании OR с большим количеством условий (более 5-7) запрос может стать менее производительным, чем эквивалентный вариант с IN. Это связано с тем, как оптимизирует план выполнения.

3. Динамические перечисления: параметры и массивы

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

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

// В коде перед выполнением запроса:

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

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

МассивВидов.Добавить(Перечисление.ВидыНоменклатуры.Услуга);

// В тексте запроса:

ВЫБРАТЬ

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

ИЗ

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

ГДЕ

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

Важные моменты:

  • 📌 Параметр &МассивВидов должен быть объявлен в запросе как Тип: Массив.
  • 🔄 Если массив пустой, условие В (&МассивВидов) вернет все записи (это распространенная ошибка!). Чтобы избежать этого, добавьте проверку:
    ГДЕ
    

    &МассивВидов.Количество() > 0

    И Номенклатура.ВидНоменклатуры В (&МассивВидов)

  • 🚀 Для больших массивов (более 100 элементов) лучше использовать временные таблицы — это ускорит выполнение.

Массив не пустой|Все элементы массива одного типа|Параметр объявлен в запросе с правильным типом|Добавлена защита от пустого массива-->

4. Временные таблицы для сложных перечислений

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

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

// 1. Создаем временную таблицу с нужными контрагентами

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

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

"ВЫБРАТЬ

| КонтрагентыСписок.Ссылка КАК Контрагент

|ИЗ

| &ТаблицаКонтрагентов КАК КонтрагентыСписок";

Запрос.УстановитьПараметр("ТаблицаКонтрагентов", ТаблицаКонтрагентов); // ТаблицаЗначений с колонкой "Ссылка"

// 2. Основной запрос с присоединением временной таблицы

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

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

"ВЫБРАТЬ

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

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

| Документ.Контрагент.Наименование

|ИЗ

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

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

| ПО Документ.Контрагент = ФильтрКонтрагентов.Контрагент";

Запрос.УстановитьВременнуюТаблицу("ВТКонтрагенты", РезультатПервогоЗапроса);

Преимущества этого подхода:

Критерий Оператор IN Временная таблица
Производительность при 1000+ значений Низкая (запрос может "зависнуть") Высокая (оптимизировано СУБД)
Гибкость (дополнительные поля) Ограничена (только сравнение) Высокая (можно добавлять условия)
Читаемость кода Высокая для простых случаев Средняя (требует двух запросов)
⚠️ Внимание: При использовании временных таблиц в 1С:Предприятие 8.3 важно следить за транзакциями. Если временная таблица создается в одной транзакции, а используется в другой, это приведет к ошибке. Все операции с временными таблицами должны выполняться в рамках одной транзакции.

5. Перечисления в конструкторе запросов: визуальное создание

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

Пошаговая инструкция:

  1. Откройте конструктор запросов (Файл → Новый → Запрос или через контекстное меню объекта).
  2. Добавьте нужные таблицы (например, Документ.РеализацияТоваровУслуг).
  3. В разделе Условия выберите поле для фильтрации (например, ВидОперации).
  4. В выпадающем списке операторов выберите В списке (эквивалент IN).
  5. Нажмите на кнопку ... рядом с полем значения и выберите нужные элементы из справочника или перечисления.
  6. Сгенерируйте текст запроса и при необходимости отредактируйте его вручную.

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

ВЫБРАТЬ

РеализацияТоваровУслуг.Ссылка КАК Ссылка,

РеализацияТоваровУслуг.Номер КАК Номер

ИЗ

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

ГДЕ

РеализацияТоваровУслуг.ВидОперации В (

ЗНАЧЕНИЕ(Перечисление.ВидыОперацийРеализации.Продажа),

ЗНАЧЕНИЕ(Перечисление.ВидыОперацийРеализации.Комиссия)

)

💡

Если в конструкторе не отображаются нужные значения для перечисления, проверьте права доступа текущего пользователя к справочникам и перечислениям в режиме 1С:Предприятие.

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

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

Ошибка 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 значений, рассмотрите вариант с временной таблицей.
  • 🔄 Порядок условий: Размещайте условия с перечислениями как можно ближе к началу секции ГДЕ, чтобы СУБД могла раньше отфильтровать данные.
  • 🛠 Анализ плана: Используйте ОбъяснитьЗапрос() для проверки, как оптимизирует ваш запрос с перечислениями.

Пример оптимизированного запроса с учетом этих рекомендаций:

ВЫБРАТЬ ПЕРВЫЕ 100

Документ.Дату,

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

Документ.СуммаДокумента

ИЗ

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

ГДЕ

Документ.Организация = &ТекущаяОрганизация

И Документ.Дату МЕЖДУ &ДатуНачала И &ДатуОкончания

И Документ.Контрагент В (&СписокКонтрагентов) // Перечисление ближе к началу

УПОРЯДОЧИТЬ ПО

Документ.Дату УБЫВ

Критическая ошибка: в запросах с большими перечислениями (более 1000 элементов) никогда не используйте конструкцию НЕ В (&Список) — это приводит к полному сканированию таблицы и падению производительности. Вместо этого используйте ВНУТРЕННЕЕ СОЕДИНЕНИЕ с временной таблицей исключаемых значений.

8. Продвинутые техники: перечисления в объединениях и подзапросах

В сложных запросах перечисления могут использоваться не только в секции ГДЕ, но и в других конструкциях. Рассмотрим продвинутые сценарии.

Перечисления в секции ИМЕЮЩИЕ

Если нужно отфильтровать сгруппированные данные по набору значений, используйте ИМЕЮЩИЕ с IN:

ВЫБРАТЬ

Контрагент.Наименование,

СУММА(Документ.СуммаДокумента) КАК ОбщаяСумма

ИЗ

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

ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.Контрагенты КАК Контрагент

ПО Документ.Контрагент = Контрагент.Ссылка

ГДЕ

Документ.Дату > &ДатаНачала

ГРУППИРОВАТЬ ПО

Контрагент.Наименование

ИМЕЮЩИЕ

Контрагент.Наименование В (&СписокКрупныхКонтрагентов)

Перечисления в подзапросах с ВЫРАЗИТЬ

Для динамического формирования списка значений прямо в запросе используйте ВЫРАЗИТЬ:

ВЫБРАТЬ

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

ИЗ

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

ГДЕ

Номенклатура.ВидНоменклатуры В (

ВЫРАЗИТЬ(

ВЫБРАТЬ РАЗЛИЧНЫЕ Вид.Ссылка ИЗ Справочник.ВидыНоменклатуры КАК Вид

ГДЕ Вид.ЭтоГруппа = ЛОЖЬ

КАК СписокВидов)

)

Перечисления в объединениях (ОБЪЕДИНИТЬ)

При объединении нескольких выборок можно фильтровать каждую часть отдельно:

ВЫБРАТЬ

Документ.Номер КАК Документ,

"Продажа" КАК ТипОперации

ИЗ

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

ГДЕ

Документ.ВидОперации = ЗНАЧЕНИЕ(Перечисление.ВидыОперацийРеализации.Продажа)

ОБЪЕДИНИТЬ ВСЕ

ВЫБРАТЬ

Документ.Номер КАК Документ,

"Комиссия" КАК ТипОперации

ИЗ

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

ГДЕ

Документ.ВидОперации = ЗНАЧЕНИЕ(Перечисление.ВидыОперацийРеализации.Комиссия)

💡

При использовании перечислений в объединениях (ОБЪЕДИНИТЬ) фильтрацию лучше применять к каждой части отдельно — это ускорит выполнение запроса за счет раннего отсечения ненужных данных.

FAQ: Частые вопросы по перечислениям в запросах 1С

Можно ли в одном перечислении использовать значения разных типов, например строки и числа?

Нет, это приведет к ошибке выполнения запроса. Все значения в перечислении должны быть одного типа. Если нужно сравнивать разные типы, приведите их к одному с помощью функций СТРОКА(), ЧИСЛО() или ДАТАВРЕМЯ().

Как передать в запрос перечисление из 10 000 элементов?

Для больших перечислений (более 1000 элементов) не рекомендуется использовать оператор IN — это приведет к падению производительности. Оптимальное решение:

  1. Загрузите значения во временную таблицу.
  2. Присоедините её к основному запросу через ВНУТРЕННЕЕ СОЕДИНЕНИЕ.

Пример кода смотрите в разделе "Временные таблицы для сложных перечислений".

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

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

  • Отсутствует индекс на поле, по которому идет фильтрация.
  • Перечисление содержит слишком много элементов (более 1000).
  • Условие с IN расположено в конце секции ГДЕ, из-за чего СУБД не может оптимизировать план выполнения.
  • В перечислении используются сложные выражения (например, вычисления над полями).

Решения:

  • Добавьте индексы на фильтруемые поля.
  • Для больших перечислений используйте временные таблицы.
  • Перенесите условие с IN ближе к началу секции ГДЕ.
  • Проанализируйте план запроса с помощью ОбъяснитьЗапрос().
Как в конструкторе запросов задать перечисление для поля типа "Справочник"?

В конструкторе:

  1. Добавьте таблицу с нужным справочником.
  2. В разделе Условия выберите поле справочника.
  3. В операторе выберите В списке.
  4. Нажмите на кнопку ... рядом с полем значения и выберите элементы из справочника.

Если нужные элементы не отображаются в списке, проверьте:

  • Права доступа к справочнику.
  • Отборы, установленные в форме справочника.
  • Тип поля (например, если поле имеет тип СправочникСсылка.Контрагенты, нельзя выбирать элементы из Справочник.Номенклатура).
Что делать, если при выполнении запроса с перечислением возникает ошибка "Типы не совпадают"?

Эта ошибка означает, что:

  • В перечислении смешаны значения разных типов (например, строка и число).
  • Тип параметра запроса не соответствует типу поля (например, параметр объявлен как Строка, а поле имеет тип Число).
  • Для ссылочных типов переданы значения из разных справочников (например, в одном перечислении ссылки на Контрагентов и Номенклатуру).

Решения:

  • Проверьте типы всех значений в перечислении с помощью ТипЗнч().
  • Убедитесь, что параметр запроса имеет правильный тип (например, Тип: СправочникСсылка.Контрагенты).
  • Для ссылочных типов используйте явное приведение типов с помощью ЗНАЧЕНИЕ().