Как написать условие отбора в запросе 1С
Содержание
Условие отбора в запросе 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);
Результат = Запрос.Выполнить();
Что здесь важно:
- Имя в тексте (
&Склад) и имя в методе ("Склад") должны совпадать, без амперсанда во втором случае. - Тип значения определяется тем, что вы передали. Если ждёте ссылку, а передали строку — отбор не сработает молча (строк не будет), ошибки платформа не покажет.
- Параметр можно использовать в тексте несколько раз — задавать его нужно один раз.
- Параметры работают не только в
ГДЕ, но и в параметрах виртуальных таблиц (как&Складв скобках после.Остаткивыше) — там отбор применяется до расчёта итогов и работает быстрее.
Ещё пара деталей, на которых спотыкаются:
- В параметр можно передать
Массив,СписокЗначений, таблицу значений или результат другого запроса — для оператораВэто основной способ задать список. Пустой массив при этом даёт пустую выборку, а не ошибку: учитывайте это, если список может оказаться незаполненным. - Если параметр не установить, а в тексте он используется, платформа при выполнении запроса сообщит об ошибке «Параметр не задан». Проверяйте, что все
&Имяиз текста получили значение. - Значение параметра можно задать и после формирования текста, и до — важен только момент до вызова
Выполнить().
Для списка значений передавайте в параметр Массив или СписокЗначений — это пригодится для оператора В из следующего раздела.
Функции отбора: В, МЕЖДУ, ПОДОБНО
Три конструкции закрывают большинство нестандартных условий.
Оператор В — проверка вхождения в список. Заменяет цепочку ИЛИ:
ГДЕ
Номенклатура.ВидНоменклатуры В (&СписокВидов)
Запрос.УстановитьПараметр("СписокВидов", МассивВидов);
Вместо списка можно подставить вложенный запрос (см. следующий раздел). Есть вариант НЕ В (...) — исключение из списка. Для иерархических справочников работает В ИЕРАРХИИ (&Группа) — отберёт элемент и всё, что вложено в него на любую глубину.
Оператор МЕЖДУ — диапазон включительно. Удобен для дат и чисел:
ГДЕ
Продажи.Период МЕЖДУ &ДатаНачала И &ДатаКонца
Границы входят в диапазон. Для периода по дням помните про время: 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 или ЕСТЬNULL(Поле, ...).МЕЖДУ передавайте КонецДня(Дата).ГОД(Дата)). Перенесите отбор на индекс или в параметры виртуальной таблицы.Частые вопросы
Чем `ГДЕ` отличается от параметров виртуальной таблицы регистра?
ГДЕ отсекает строки из готового результата. Для регистров отбор по измерениям выгоднее ставить в параметрах таблицы.Можно ли в условии сравнивать два поля одной таблицы?
ГДЕ Остатки.Зарезервировано > Остатки.КоличествоОстаток — корректное условие. Слева и справа могут стоять любые выражения с полями.Как отобрать строки, где поле не заполнено?
ЗНАЧЕНИЕ(...ПустаяСсылка). Если поле стало пустым после левого соединения — это NULL, и проверять нужно оператором ЕСТЬ NULL.Почему условие с `ПОДОБНО` работает медленно?
% ("%текст%") заставляет читать таблицу целиком — индекс не применяется. Если ищете по началу значения ("текст%"), индекс сработает и запрос ускорится.В чём разница между `В` и `В ИЕРАРХИИ`?
В (&Список) проверяет точное вхождение в переданный список значений. В ИЕРАРХИИ (&Группа) для иерархического справочника отберёт саму группу и все вложенные элементы на любую глубину.