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

Отборы в используются везде: от формирования отчётов до сложных аналитических выборок. Однако многие разработчики ограничиваются примитивными конструкциями вроде ГДЕ Номенклатура = &Номенклатура, не подозревая, что язык запросов поддерживает регулярные выражения, вложенные условия и даже динамические параметры с проверкой типов. Мы покажем, как избежать типичных ошибок (например, сравнения дат в неверном формате) и ускорить выполнение запросов за счёт правильной индексации.

Особое внимание уделим отборам по виртуальным таблицам — это одна из самых сложных тем, где ошибки приводят к непредсказуемым результатам. Вы узнаете, почему иногда условие ГДЕ Период МЕЖДУ &Начало И &Конец работает не так, как ожидается, и как это исправить.

1. Базовый синтаксис отборов в запросах 1С

Любой отбор в запросе начинается с ключевого слова ГДЕ (или WHERE в английской нотации). После него следуют условия, соединённые логическими операторами И, ИЛИ, НЕ. Например:

ВЫБРАТЬ

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

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

ИЗ

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

ГДЕ

Номенклатура.ПометкаУдаления = ЛОЖЬ

И Номенклатура.ЭтоГруппа = ЛОЖЬ

Если первое условие в отборе выполняется дольше остальных, запрос будет тормозить. Например, проверка Номенклатура.Артикул НЕ ПУСТАЯ СТРОКА работает быстрее, чем Номенклатура.Родитель = &Родитель, если поле Артикул проиндексировано.

  • 📌 Равенство: =, <> (не равно). Пример: ГДЕ Статус = &Статус
  • 📅 Даты: МЕЖДУ, >=, <=. Пример: ГДЕ Дата МЕЖДУ &Начало И &Конец
  • 🔍 Поиск по строке: СОДЕРЖИТ, НАЧИНАЕТСЯ. Пример: ГДЕ Наименование СОДЕРЖИТ "Тест"
  • 📊 Null-значения: ЕСТЬ NULL, ЗНАЧЕНИЕ ЗАПОЛНЕНО. Пример: ГДЕ Контрагент ЕСТЬ NULL
⚠️ Внимание: При сравнении дат всегда используйте параметры (&Дата), а не жёстко прописанные значения. Иначе запрос не будет использовать индексы, и его выполнение замедлится в 10–100 раз.

2. Сложные условия: И, ИЛИ, НЕ и скобки

Когда требуется комбинировать несколько условий, важно правильно расставлять скобки. Без них логика отбора может исказиться. Например:

ГДЕ

(Статус = ЗНАЧЕНИЕ(Перечисление.СтатусыДокументов.Проведен)

ИЛИ Статус = ЗНАЧЕНИЕ(Перечисление.СтатусыДокументов.Оплачен))

И Дата > &МинимальнаяДата

Здесь скобки гарантируют, что сначала проверяется статус, а потом — дата. Без них условие сработает как (Статус = Проведен ИЛИ Дата > &МинимальнаяДата), что приведёт к выборке лишних данных.

Оператор НЕ часто вызывает путаницу. Например, ГДЕ НЕ (Статус = Проведен) и ГДЕ Статус <> Проведен — не одно и то же! Первое условие исключает только значение "Проведен", а второе — все документы с любым другим статусом, включая NULL.

📊 Какой оператор вы используете чаще в отборах?
И
ИЛИ
НЕ
МЕЖДУ
  • 🔄 Приоритет операторов: НЕИИЛИ. Всегда используйте скобки для ясности.
  • 🚫 Отрицание: НЕ (А ИЛИ Б) эквивалентно (НЕ А) И (НЕ Б) (закон де Моргана).
  • 📈 Производительность: Условия с И выполняются быстрее, чем с ИЛИ, если они стоят первыми.

3. Отборы по датам и периодам

Работа с датами — одна из самых частых задач в . Ошибки здесь приводят к пропуску записей или, наоборот, к выборке лишних данных. Например, условие ГДЕ Дата = &ТекущаяДата не учтёт время, если поле Дата имеет тип ДатаВремя. Правильнее писать:

ГДЕ Дата >= НАЧАЛОДНЯ(&ТекущаяДата)

И Дата < КОНЕЦДНЯ(&ТекущаяДата) + 1

Для работы с периодами используйте МЕЖДУ, но помните: это условие включает граничные значения. Если нужно исключить крайние даты, пишите явно:

ГДЕ Дата > &Начало

И Дата < &Конец

Задача Неверный отбор Правильный отбор
Выборка за текущий день Дата = ТЕКУЩАЯДАТА() Дата >= НАЧАЛОДНЯ(ТЕКУЩАЯДАТА())
Выборка за месяц Дата МЕЖДУ НачалоМесяца(Дата) И КонецМесяца(Дата) Дата >= НачалоМесяца(&Дата) И Дата <= КонецМесяца(&Дата)
Исключение выходных ДЕНЬНЕДЕЛИ(Дата) <> 6 И ДЕНЬНЕДЕЛИ(Дата) <> 7 НЕ (ДЕНЬНЕДЕЛИ(Дата) В (6, 7))
⚠️ Внимание: Функции вроде НАЧАЛОДНЯ() или ДЕНЬНЕДЕЛИ() нельзя применять к полям в условиях отбора — это блокирует использование индексов. Всегда вычисляйте такие значения заранее и передавайте как параметры.

4. Отборы по виртуальным таблицам

Виртуальные таблицы (например, Документ.РеализацияТоваровУслуг.Обороты) требуют особого подхода. Здесь нельзя использовать стандартные условия для полей, которые не существуют в физической таблице. Например, такой запрос вызовет ошибку:

ВЫБРАТЬ

Обороты.Номенклатура,

Обороты.Количество

ИЗ

Документ.РеализацияТоваровУслуг.Обороты(&Начало, &Конец,) КАК Обороты

ГДЕ

Обороты.Сумма > 1000

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

ВЫБРАТЬ

Обороты.Номенклатура,

Обороты.Количество

ИЗ

Документ.РеализацияТоваровУслуг.Обороты(&Начало, &Конец,) КАК Обороты

ГДЕ

Обороты.Количество * Обороты.Цена > 1000

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

Указаны все обязательные параметры (период, измерения)|

Поля в условии существуют в физической таблице|

Используются индексируемые поля для отбора|

Проверена логика вычисляемых полей-->

5. Динамические отборы и параметры

В реальных задачах условия отбора часто формируются динамически — например, в зависимости от прав пользователя или настроек отчёта. Для этого используйте динамические параметры и конструкцию ЕСЛИ.. ТОГДА.. ИНАЧЕ:

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

"ВЫБРАТЬ

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

ИЗ

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

ГДЕ

" + ?(НужноТолькоАктивные, "Номенклатура.ПометкаУдаления = ЛОЖЬ", "") +

?(ФильтрПоГруппе <> Неопределено, " И Номенклатура.Родитель = &Родитель", "");

Важно: при динамическом формировании текста запроса всегда экранируйте параметры, чтобы избежать SQL-инъекций. В для этого используйте метод ЭкранироватьСтроку():

Параметр = ЭкранироватьСтроку(ПользовательскийВвод, Ложь);

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

Для сложных динамических отборов удобно использовать временные таблицы. Например, если нужно отфильтровать номенклатуру по списку артикулов:

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

ВТ_Артикулы = Новый ТаблицаЗначений;

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

// Заполняем данными

Для Каждого Артикул Из СписокАртикулов Цикл

Строка = ВТ_Артикулы.Добавить();

Строка.Артикул = Артикул;

КонецЦикла;

// Используем во временной таблице запроса

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

"ВЫБРАТЬ

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

ИЗ

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

ГДЕ

Номенклатура.Артикул В (&ВТ_Артикулы)";

Запрос.УстановитьПараметр("ВТ_Артикулы", ВТ_Артикулы);

💡

Если динамический отбор становится слишком сложным, разбейте его на несколько запросов с промежуточными временными таблицами. Это упростит отладку и ускорит выполнение.

6. Оптимизация отборов: индексы и планы выполнения

Даже правильно написанный отбор может работать медленно, если не учитывать индексы. В индексы создаются автоматически для полей, отмеченных в конфигураторе как индексируемые, но их можно добавлять и вручную через ИНДЕКСИРОВАТЬ ПО:

ВЫБРАТЬ

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

ИЗ

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

ИНДЕКСИРОВАТЬ ПО

Дата,

Контрагент

ГДЕ

ЗаказПокупателя.Дата МЕЖДУ &Начало И &Конец

И ЗаказПокупателя.Контрагент = &Контрагент

Чтобы проверить, использует ли запрос индексы, включите план выполнения:

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

Запрос.Текст = "ВЫБРАТЬ..";

Запрос.ПланВопроса = Истина; // Включаем вывод плана

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

В плане обратите внимание на:

  • 🔍 Полное сканирование (Seq Scan) — означает, что индексы не используются.
  • Индексное сканирование (Index Scan) — оптимальный вариант.
  • 📉 Стоимость операции (Cost) — чем выше, тем медленнее запрос.
⚠️ Внимание: Если в условии отбора используется функция (например, НАЧИНАЕТСЯ(Наименование, "А")), индекс по полю Наименование использоваться не будет. Переносите такие проверки в секцию ГДЕ через параметры или используйте временные таблицы.
💡

Индексы ускоряют отборы только если условия написаны в формате "поле = значение" или "поле > значение". Функции, вычисления и OR блокируют использование индексов.

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

Даже опытные разработчики допускают ошибки при работе с отборами. Вот самые распространённые:

  1. Сравнение разных типов. Например, ГДЕ Код = "123", где Код — число, а "123" — строка. Это приведёт к неявному приведению типов и медленному выполнению. Всегда следите за типами параметров:
  2. Запрос.УстановитьПараметр("Код", Число(123)); // Правильно
  3. Игнорирование NULL. Условие ГДЕ Поле <> Значение не вернёт строки, где Поле = NULL. Используйте ГДЕ Поле <> Значение ИЛИ Поле ЕСТЬ NULL.
  4. Неправильные даты. Условие ГДЕ Дата = &Дата не сработает для поля типа ДатаВремя, так как время будет учитываться. Используйте МЕЖДУ НАЧАЛОДНЯ() И КОНЕЦДНЯ().
  5. Избыточные условия. Например, ГДЕ (А И Б) ИЛИ (А И В) можно упростить до ГДЕ А И (Б ИЛИ В), что ускорит выполнение.

Ещё одна частая проблема — неявные соединения в условиях. Например:

ВЫБРАТЬ

Заказ.Номер

ИЗ

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

Справочник.Контрагенты КАК Контрагент

ГДЕ

Заказ.Контрагент = Контрагент.Ссылка

И Контрагент.Наименование СОДЕРЖИТ "ООО"

Такой запрос создаёт CROSS JOIN (декартово произведение), что крайне неэффективно. Замените его на явное соединение:

ВЫБРАТЬ

Заказ.Номер

ИЗ

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

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

ПО Заказ.Контрагент = Контрагент.Ссылка

ГДЕ

Контрагент.Наименование СОДЕРЖИТ "ООО"

8. Продвинутые техники: регулярные выражения и полнотекстовый поиск

Для сложных строковых отборов в 1С 8.3.14+ доступны регулярные выражения через функцию РЕГВЫРАЖ(). Например, чтобы найти артикулы в формате ABC-123:

ГДЕ РЕГВЫРАЖ(Артикул, "^[A-Z]{3}-\d{3}$", Истина) = Истина

Параметры функции:

  • ^ — начало строки,
  • [A-Z]{3} — ровно 3 заглавные буквы,
  • -\d{3}$ — тире и 3 цифры до конца строки.

Для полнотекстового поиска (например, поиска по словам с учётом морфологии) используйте оператор ПОДОБНО или специализированные функции:

ГДЕ ПОЛНЫЙТЕКСТ(Наименование, "телефон смартфон")

Это особенно полезно для справочников с большим количеством записей, где стандартный СОДЕРЖИТ работает слишком медленно.

Как ускорить полнотекстовый поиск?

Для ускорения полнотекстового поиска в 1С:

1. Настройте полнотекстовые индексы в конфигураторе (раздел "Полнотекстовый поиск").

2. Используйте предварительную фильтрацию по другим полям (например, по группе номенклатуры).

3. Ограничивайте количество возвращаемых строк с помощью ПЕРВЫЕ 100.

FAQ: Ответы на частые вопросы по отборам в 1С

1. Почему мой запрос игнорирует условие отбора?

Чаще всего это происходит из-за:

  • Несовпадения типов (например, сравнение числа со строкой).
  • Ошибок в логике условий (пропущенные скобки или операторы).
  • Использования функций в отборе (например, ЛЕВ(Наименование, 3) = "АБВ" блокирует индексы).

Проверьте план выполнения запроса, чтобы увидеть, как именно работает ваш отбор.

2. Как сделать отбор по нескольким значениям (например, список номенклатуры)?

Используйте оператор В с массивом или временной таблицей:

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

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

3. Можно ли в отборе использовать подзапросы?

Да, но осторожно. Подзапросы в условиях ГДЕ могут сильно замедлить выполнение. Пример:

ГДЕ Контрагент В (

ВЫБРАТЬ Контрагент

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

ГДЕ Дата > &МинимальнаяДата

)

Лучше заменить такой подзапрос на соединение или временную таблицу.

4. Как отладить сложный отбор?

Используйте следующие техники:

  • Выводите план выполнения (Запрос.ПланВопроса = Истина).
  • Разбивайте запрос на части и проверяйте каждую отдельно.
  • Используйте Сообщить() для вывода промежуточных результатов.

5. Почему отбор по виртуальной таблице возвращает неверные данные?

Виртуальные таблицы (например, обороты или остатки) зависят от переданных параметров. Типичные ошибки:

  • Не указан период (Обороты(&Начало, &Конец)).
  • Неверный порядок параметров (например, перепутаны начало и конец периода).
  • Отбор по вычисляемым полям (например, СуммаНДС), которые не существуют в физической таблице.

Всегда сверяйтесь с документацией по конкретной виртуальной таблице.