Как написать условие отбора в запросе 1С

· 11 мин

Содержание

Условие отбора в запросе 1С задаёт секция ГДЕ. Частая ошибка новичка — фильтровать выборку уже в коде, перебирая результат запроса циклом. Так в память попадают лишние строки, а сервер делает лишнюю работу. Правильно отсекать ненужные данные на уровне запроса: напишите условие в секции ГДЕ, и платформа отдаст только подходящие строки.

Ниже разберём синтаксис секции ГДЕ, логические операторы И, ИЛИ, НЕ, специальные конструкции В, МЕЖДУ, ПОДОБНО, передачу значений через параметры, вложенные запросы и сборку условий во время выполнения. Все примеры — на русском варианте языка запросов платформы 8.3.

Секция ГДЕ: базовый синтаксис

Секция ГДЕ идёт после ИЗ и перед СГРУППИРОВАТЬ ПО или УПОРЯДОЧИТЬ ПО. В ней вы перечисляете выражения, которые должны быть истинными для каждой строки результата.

ВЫБРАТЬ
    Номенклатура.Ссылка,
    Номенклатура.Наименование,
    Номенклатура.Артикул
ИЗ
    Справочник.Номенклатура КАК Номенклатура
ГДЕ
    Номенклатура.ПометкаУдаления = ЛОЖЬ

В условии доступны операторы сравнения: =, <> (не равно), >, <, >=, <=. Слева обычно стоит поле таблицы, справа — значение, параметр или другое поле.

Несколько правил, которые сразу уберут половину ошибок:

  • 🔹 Сравнение булева поля пишите явно: Номенклатура.ПометкаУдаления = ЛОЖЬ, а не просто НЕ Номенклатура.ПометкаУдаления (второе тоже работает, но первое читается однозначнее).
  • 🔹 Строки заключайте в двойные кавычки: Номенклатура.Наименование = "Болт М8".
  • 🔹 Даты задавайте литералом ДАТАВРЕМЯ(2026, 1, 1) или через параметр — склеивать дату из строки в тексте запроса нельзя.
  • 🔹 Для перечислений указывайте полный путь: Документ.ВидОперации = ЗНАЧЕНИЕ(Перечисление.ВидыОперацийПоступление.Товары).

Функция ЗНАЧЕНИЕ() — отдельная тема. Через неё в текст запроса подставляют предопределённые элементы: значения перечислений, пустые ссылки ЗНАЧЕНИЕ(Справочник.Номенклатура.ПустаяСсылка), предопределённые элементы справочников и планов счетов. Это удобно, когда значение известно на этапе написания запроса и не меняется.

Если поле составного типа (например, Регистратор в движениях регистра), сравнивать его со ссылкой конкретного типа можно напрямую — платформа сама разберётся с типом.

Логические операторы И, ИЛИ, НЕ

Одно условие редко решает задачу. Несколько выражений объединяют операторами И, ИЛИ, НЕ.

ГДЕ
    Номенклатура.ПометкаУдаления = ЛОЖЬ
    И Номенклатура.ЭтоГруппа = ЛОЖЬ
    И Номенклатура.Родитель = &Группа

Оператор И требует истинности всех условий, ИЛИ — хотя бы одного, НЕ инвертирует результат.

Главная ловушка — приоритет операций. И вычисляется раньше ИЛИ, как умножение раньше сложения. Поэтому запрос

ГДЕ
    Товар.Склад = &Склад
    И Товар.Количество > 0
    ИЛИ Товар.Зарезервировано > 0

вернёт не то, что ожидалось: платформа прочитает его как (Склад = &Склад И Количество > 0) ИЛИ Зарезервировано > 0. Строки с резервом попадут в результат независимо от склада. Чтобы сгруппировать условия по своему замыслу, ставьте скобки:

ГДЕ
    Товар.Склад = &Склад
    И (Товар.Количество > 0 ИЛИ Товар.Зарезервировано > 0)

Когда в условии встречается ИЛИ, перечитайте секцию ГДЕ ещё раз и расставьте скобки явно, даже если кажется, что приоритет и так очевиден. Это самая частая причина «запрос выдаёт лишние строки».

Оператор НЕ удобен для инверсии сложного блока: НЕ (Товар.Количество > 0 И Товар.Склад = &Склад). Но с полями, которые могут быть NULL, он ведёт себя неочевидно.

Дело в том, что язык запросов работает по трёхзначной логике: выражение бывает истинным, ложным или неопределённым (NULL). Сравнение Поле > 0, где Поле равно NULL, даёт не «ложь», а именно NULL. В секцию результата попадают только строки, где всё условие истинно — ни ложные, ни неопределённые. Поэтому НЕ (Поле > 0) для пустого поля тоже не даст истины: отрицание NULL остаётся NULL. Если нужно, чтобы пустые значения считались подходящими, пропишите это явно: ЕСТЬNULL(Поле, 0) > 0 или добавьте ИЛИ Поле ЕСТЬ NULL.

Порядок условий внутри И на результат не влияет, а вот на читаемость — да. Ставьте первым самое «отсекающее» условие (например, отбор по складу), а уточнения — ниже. Платформа сама выберет порядок проверки в плане запроса, но коллеге, который будет читать код, так понятнее.

Передача значений через параметры запроса

Подставлять значения прямо в текст запроса конкатенацией строк — плохая практика: код становится уязвимым к ошибкам экранирования и типов, а платформа не может переиспользовать план. Правильный путь — параметры. Параметр в тексте начинается с амперсанда &, а значение задаётся методом УстановитьПараметр.

Запрос = Новый Запрос;
Запрос.Текст =
    "ВЫБРАТЬ
    |    Остатки.Номенклатура,
    |    Остатки.КоличествоОстаток
    |ИЗ
    |    РегистрНакопления.ТоварыНаСкладах.Остатки(&МоментВремени, Склад = &Склад) КАК Остатки
    |ГДЕ
    |    Остатки.КоличествоОстаток > &МинОстаток";

Запрос.УстановитьПараметр("МоментВремени", КонецДня(ТекущаяДата()));
Запрос.УстановитьПараметр("Склад", ВыбранныйСклад);
Запрос.УстановитьПараметр("МинОстаток", 0);

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

Что здесь важно:

  1. Имя в тексте (&Склад) и имя в методе ("Склад") должны совпадать, без амперсанда во втором случае.
  2. Тип значения определяется тем, что вы передали. Если ждёте ссылку, а передали строку — отбор не сработает молча (строк не будет), ошибки платформа не покажет.
  3. Параметр можно использовать в тексте несколько раз — задавать его нужно один раз.
  4. Параметры работают не только в ГДЕ, но и в параметрах виртуальных таблиц (как &Склад в скобках после .Остатки выше) — там отбор применяется до расчёта итогов и работает быстрее.

Ещё пара деталей, на которых спотыкаются:

  • В параметр можно передать Массив, СписокЗначений, таблицу значений или результат другого запроса — для оператора В это основной способ задать список. Пустой массив при этом даёт пустую выборку, а не ошибку: учитывайте это, если список может оказаться незаполненным.
  • Если параметр не установить, а в тексте он используется, платформа при выполнении запроса сообщит об ошибке «Параметр не задан». Проверяйте, что все &Имя из текста получили значение.
  • Значение параметра можно задать и после формирования текста, и до — важен только момент до вызова Выполнить().

Для списка значений передавайте в параметр Массив или СписокЗначений — это пригодится для оператора В из следующего раздела.

Функции отбора: В, МЕЖДУ, ПОДОБНО

Три конструкции закрывают большинство нестандартных условий.

Оператор В — проверка вхождения в список. Заменяет цепочку ИЛИ:

ГДЕ
    Номенклатура.ВидНоменклатуры В (&СписокВидов)
Запрос.УстановитьПараметр("СписокВидов", МассивВидов);

Вместо списка можно подставить вложенный запрос (см. следующий раздел). Есть вариант НЕ В (...) — исключение из списка. Для иерархических справочников работает В ИЕРАРХИИ (&Группа) — отберёт элемент и всё, что вложено в него на любую глубину.

Оператор МЕЖДУ — диапазон включительно. Удобен для дат и чисел:

ГДЕ
    Продажи.Период МЕЖДУ &ДатаНачала И &ДатаКонца

Границы входят в диапазон. Для периода по дням помните про время: 01.03.2026 без времени — это 00:00:00, и документы, проведённые днём последней даты, в отбор не попадут. Передавайте в верхнюю границу КонецДня(ДатаКонца).

Оператор ПОДОБНО — поиск по шаблону строки. Спецсимволы:

СимволЧто означает
%любое количество любых символов (в том числе ноль)
_ровно один любой символ
[абв]один символ из набора
[а-я]один символ из диапазона
ГДЕ
    Номенклатура.Наименование ПОДОБНО &Шаблон
Запрос.УстановитьПараметр("Шаблон", "%болт%");

Шаблон "%болт%" найдёт наименование, где «болт» встречается в любом месте. Если сам процент или подчёркивание нужно искать как символ, экранируйте его и объявите спецсимвол:

ГДЕ
    Товар.Код ПОДОБНО "100\%" СПЕЦСИМВОЛ "\"

ПОДОБНО с ведущим % ("%болт%") не использует индекс — платформа читает всю таблицу. Для больших справочников это заметно на времени. Если можно искать по началу строки ("болт%"), индекс сработает.

Подзапросы и составные условия

Когда отбор зависит от данных другой таблицы, в правой части условия ставят вложенный запрос. Классический случай — «номенклатура, которая продавалась за период»:

ВЫБРАТЬ
    Номенклатура.Ссылка,
    Номенклатура.Наименование
ИЗ
    Справочник.Номенклатура КАК Номенклатура
ГДЕ
    Номенклатура.Ссылка В (
        ВЫБРАТЬ РАЗЛИЧНЫЕ
            Продажи.Номенклатура
        ИЗ
            РегистрНакопления.Продажи КАК Продажи
        ГДЕ
            Продажи.Период МЕЖДУ &Нач И &Кон)

Несколько приёмов:

  • 🔸 РАЗЛИЧНЫЕ во вложенном запросе убирает дубли — иначе оператор В всё равно сравнит корректно, но запрос обработает лишние строки.
  • 🔸 Отрицание НЕ В (...) отбирает то, чего в подзапросе нет: «номенклатура, которая ни разу не продавалась».
  • 🔸 Вложенный запрос может ссылаться на поля внешнего (коррелированный подзапрос), но на больших данных это медленно — чаще выгоднее соединение таблиц через СОЕДИНЕНИЕ.

Отдельно стоит ВЫБОР КОГДА ... ТОГДА ... ИНАЧЕ ... КОНЕЦ — аналог условного выражения. В секции ГДЕ он нужен редко, но помогает, когда условие зависит от значения самого поля:

ГДЕ
    ВЫБОР
        КОГДА Документ.Вид = ЗНАЧЕНИЕ(Перечисление.ВидыДокументов.Расход)
            ТОГДА Документ.Сумма > &Лимит
        ИНАЧЕ ИСТИНА
    КОНЕЦ

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

Как собрать условие во время выполнения

Часто набор фильтров зависит от того, что выбрал пользователь в форме: склад указан — фильтруем по складу, не указан — берём все. Есть два подхода.

Подход 1 — условие-выключатель через параметр. Текст запроса не меняется, а «ненужное» условие нейтрализуется параметром-флагом:

ГДЕ
    (&ОтборПоСкладу = ЛОЖЬ ИЛИ Остатки.Склад = &Склад)
    И (&ОтборПоВиду = ЛОЖЬ ИЛИ Товар.ВидНоменклатуры = &Вид)
Запрос.УстановитьПараметр("ОтборПоСкладу", ЗначениеЗаполнено(Склад));
Запрос.УстановитьПараметр("Склад", Склад);
Запрос.УстановитьПараметр("ОтборПоВиду", ЗначениеЗаполнено(Вид));
Запрос.УстановитьПараметр("Вид", Вид);

Когда флаг ЛОЖЬ, первая часть скобки истинна и условие фильтр не накладывает. Текст запроса остаётся неизменным — это надёжнее, чем склейка строк, и его проще читать. Минус: платформа всё равно держит условие в плане, на очень больших таблицах это чуть дороже варианта с динамическим текстом.

Подход 2 — сборка текста условия. Текст ГДЕ формируем в коде, добавляя только нужные блоки:

МассивУсловий = Новый Массив;
Если ЗначениеЗаполнено(Склад) Тогда
    МассивУсловий.Добавить("Остатки.Склад = &Склад");
    Запрос.УстановитьПараметр("Склад", Склад);
КонецЕсли;
Если ЗначениеЗаполнено(Вид) Тогда
    МассивУсловий.Добавить("Товар.ВидНоменклатуры = &Вид");
    Запрос.УстановитьПараметр("Вид", Вид);
КонецЕсли;

ТекстГДЕ = ?(МассивУсловий.Количество() = 0, "ИСТИНА",
    СтрСоединить(МассивУсловий, " И "));
Запрос.Текст = ТекстШаблон + " ГДЕ " + ТекстГДЕ;

Даже при сборке текста сами значения всегда передавайте параметрами, а не вставляйте в строку. В текст идёт только имя поля и имя параметра — это защищает от ошибок с типами и датами и оставляет код читаемым.

Если условий много и они переиспользуются, посмотрите в сторону СхемаКомпоновкиДанных и НастройкиКомпоновкиДанных: СКД умеет сама собирать отбор из пользовательских настроек, и городить ручную сборку текста не придётся.

Оптимизация: когда условие тормозит

Условие в ГДЕ влияет на скорость напрямую. Несколько правил держат запрос быстрым.

Фильтруйте по индексированным полям. У ссылки (Ссылка), измерений регистров, полей с включённым индексированием отбор идёт по индексу. Отбор по неиндексированному реквизиту справочника заставляет читать таблицу целиком.

Не оборачивайте поле в функцию в левой части. Условие ГОД(Документ.Дата) = 2026 не использует индекс по дате — платформа вычисляет функцию для каждой строки. Замените диапазоном: Документ.Дата МЕЖДУ &НачалоГода И &КонецГода.

Переносите отбор в параметры виртуальных таблиц. Для регистров условие лучше ставить не в ГДЕ, а в скобках виртуальной таблицы:

ИЗ
    РегистрНакопления.ТоварыНаСкладах.Остатки(
        &НаДату,
        Склад = &Склад И Номенклатура В (&СписокТоваров)) КАК Остатки

Так отбор применяется до расчёта остатков, а не после — объём промежуточных данных меньше.

Для составных полей используйте ВЫРАЗИТЬ. Если поле хранит значения нескольких типов (например, Регистратор), а вам нужны только документы одного вида, приведение типом помогает индексу:

ГДЕ
    ВЫРАЗИТЬ(Движения.Регистратор КАК Документ.РеализацияТоваровУслуг).Организация = &Орг

Сравнивайте однотипные значения. Сравнение ссылки с NULL или числа со строкой не только медленно, но и даёт неожиданный результат.

Хотите понять, где запрос «спотыкается» — соберите план в консоли запросов или посмотрите технологический журнал с событием DBMSSQL. Это точнее, чем гадать по тексту.

Типичные ошибки при написании условий

Эти ошибки встречаются чаще всего — и большинство компилятор не ловит, запрос просто молча возвращает не то.

  • ❌ Забытые скобки вокруг ИЛИ. И сильнее ИЛИ, и без скобок отбор расширяется. Самый частый баг — разобран выше.
  • ❌ Сравнение с NULL через =. Выражение Поле = NULL всегда ложно, даже если поле действительно пустое. Для проверки на NULL есть оператор ЕСТЬ NULL (и ЕСТЬ НЕ NULL). NULL появляется после левых соединений, когда в правой таблице нет строки.
  • ❌ Путаница NULL, Неопределено и пустой ссылки. Это три разных значения. Поле без значения в базе — пустая ссылка (ЗНАЧЕНИЕ(Справочник.Номенклатура.ПустаяСсылка)), отсутствие строки после соединения — NULL, нетипизированное пустое поле — Неопределено. Функция ЕСТЬNULL(Поле, Подстановка) заменяет NULL на заданное значение.
  • ❌ Условие на поле после СГРУППИРОВАТЬ ПО. Фильтр по агрегату (СУММА, КОЛИЧЕСТВО) ставят не в ГДЕ, а в секции ИМЕЮЩИЕ. ГДЕ отбирает строки до группировки, ИМЕЮЩИЕ — после.
  • ❌ Склейка значений в текст запроса. Приводит к ошибкам с датами, кавычками и типами. Всегда параметры.
  • ❌ Отбор по неиндексированному реквизиту на большой таблице. Технически работает, но читает всё. Выносите часто используемые отборы на индексируемые поля.
Диагностика: выберите симптом

Частые вопросы

Чем `ГДЕ` отличается от параметров виртуальной таблицы регистра?
Параметры в скобках виртуальной таблицы применяются до расчёта итогов — на вход агрегации идёт уже отобранный набор. ГДЕ отсекает строки из готового результата. Для регистров отбор по измерениям выгоднее ставить в параметрах таблицы.
Можно ли в условии сравнивать два поля одной таблицы?
Да. ГДЕ Остатки.Зарезервировано > Остатки.КоличествоОстаток — корректное условие. Слева и справа могут стоять любые выражения с полями.
Как отобрать строки, где поле не заполнено?
Для реквизита-ссылки сравнивайте с пустой ссылкой через ЗНАЧЕНИЕ(...ПустаяСсылка). Если поле стало пустым после левого соединения — это NULL, и проверять нужно оператором ЕСТЬ NULL.
Почему условие с `ПОДОБНО` работает медленно?
Шаблон с ведущим % ("%текст%") заставляет читать таблицу целиком — индекс не применяется. Если ищете по началу значения ("текст%"), индекс сработает и запрос ускорится.
В чём разница между `В` и `В ИЕРАРХИИ`?
В (&Список) проверяет точное вхождение в переданный список значений. В ИЕРАРХИИ (&Группа) для иерархического справочника отберёт саму группу и все вложенные элементы на любую глубину.