Как задать условие отбора в запросе 1С 8.3
Содержание
Условие отбора в запросе 1С пишут в секции ГДЕ — после списка полей и источника данных. Самый короткий вид выглядит так: ГДЕ Контрагент = &Контрагент. Сравниваемое значение подставляют параметром &Имя, а сам параметр задают в коде методом УстановитьПараметр.
На практике одним равенством дело не ограничивается: нужно сравнивать числа и даты, объединять несколько условий через И и ИЛИ, отбирать по ссылке и по полю составного типа, фильтровать по списку из временной таблицы и при этом не уронить скорость. Разберём каждый случай с рабочим кодом на встроенном языке запросов.
Где размещается условие и какие операторы сравнения есть
Отбор в языке запросов живёт не только в ГДЕ. Условие можно поставить в четырёх местах, и путать их не стоит:
ГДЕ— основной фильтр строк результата.ПО— условие соединения таблиц (вЛЕВОЕ СОЕДИНЕНИЕ ... ПО).ИМЕЮЩИЕ— фильтр уже по сгруппированным (агрегированным) данным.ВЫБОР КОГДА ... ТОГДА ... ИНАЧЕ ... КОНЕЦ— условное выражение прямо в поле выборки.
Операторы сравнения в ГДЕ стандартные, но у языка запросов есть несколько своих:
| Оператор | Что проверяет | Пример |
|---|---|---|
= <> | равно / не равно | Сумма <> 0 |
> < >= <= | больше / меньше | Количество >= 10 |
МЕЖДУ ... И ... | попадание в диапазон (границы включительно) | Дата МЕЖДУ &Нач И &Кон |
В (...) | вхождение в список значений | Статус В (&Список) |
В ИЕРАРХИИ (...) | элемент и все его подчинённые в группе | Номенклатура В ИЕРАРХИИ (&Группа) |
ПОДОБНО | сравнение строки по шаблону | Наименование ПОДОБНО "%кабель%" |
ЕСТЬ NULL / ЕСТЬ НЕ NULL | поле пустое (NULL) или нет | Контрагент.Телефон ЕСТЬ NULL |
Простой пример полного запроса с отбором:
ВЫБРАТЬ
Реализация.Ссылка КАК Документ,
Реализация.СуммаДокумента КАК Сумма
ИЗ
Документ.РеализацияТоваровУслуг КАК Реализация
ГДЕ
Реализация.Проведен = ИСТИНА
И Реализация.Дата МЕЖДУ &НачалоПериода И &КонецПериода
И Реализация.СуммаДокумента > 0
В шаблоне ПОДОБНО символ % заменяет любую последовательность знаков, _ — ровно один знак. Если сам процент нужен как текст, экранируйте его: ПОДОБНО "%100\%%" СПЕЦСИМВОЛ "\".
Когда нужно не отфильтровать строку целиком, а вычислить значение по условию, применяют конструкцию ВЫБОР. Она работает прямо в списке полей и возвращает разные результаты в зависимости от проверки:
ВЫБРАТЬ
Заказ.Ссылка,
ВЫБОР
КОГДА Заказ.СуммаДокумента > 100000 ТОГДА "Крупный"
КОГДА Заказ.СуммаДокумента > 10000 ТОГДА "Средний"
ИНАЧЕ "Мелкий"
КОНЕЦ КАК Категория
ИЗ
Документ.ЗаказКлиента КАК Заказ
Отбор по агрегированным данным (сумме, количеству строк) ставят не в ГДЕ, а в секцию ИМЕЮЩИЕ — она отрабатывает после СГРУППИРОВАТЬ ПО. Например, «контрагенты, у которых продаж больше чем на миллион»: ИМЕЮЩИЕ СУММА(Продажи.Сумма) > 1000000. Поставить такое условие в ГДЕ нельзя — на момент его вычисления итог ещё не рассчитан.
Как подставить значение через параметр запроса
Прямо в текст условия значения почти никогда не вписывают — вместо этого ставят параметр с амперсандом, а передают его отдельно. Это защищает от ошибок с кавычками и типами и позволяет повторно выполнять запрос с разными данными.
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ Ссылка ИЗ Справочник.Контрагенты
|ГДЕ ИНН = &ИНН И НЕ ПометкаУдаления";
Запрос.УстановитьПараметр("ИНН", "7701234567");
Результат = Запрос.Выполнить();
Отбор по списку значений делают через В (&Параметр), где в параметр передают массив или список значений:
МассивСтатусов = Новый Массив;
МассивСтатусов.Добавить(Перечисления.СтатусыЗаказов.Отгружен);
МассивСтатусов.Добавить(Перечисления.СтатусыЗаказов.Закрыт);
Запрос.УстановитьПараметр("Статусы", МассивСтатусов);
ГДЕ Заказ.Статус В (&Статусы)
Даты в самом тексте запроса (когда они фиксированы) задают литералом ДАТАВРЕМЯ(2020, 1, 1), но чаще всё равно передают параметром. Начало и конец дня считают функциями НАЧАЛОПЕРИОДА(&Дата, ДЕНЬ) и КОНЕЦПЕРИОДА(&Дата, ДЕНЬ) — иначе отбор = &Дата поймает только записи ровно на 00:00:00.
Все параметры, которые встречаются в тексте, должны быть установлены до Выполнить(). Если забыть хотя бы один, запрос упадёт с ошибкой о неустановленном параметре. Параметр, которого нет в тексте, задавать не обязательно — лишний просто игнорируется.
Логические операторы И, ИЛИ, НЕ и порядок их вычисления
Несколько условий связывают операторами И, ИЛИ, НЕ. Ключевой момент, на котором ломается половина отборов, — приоритет. Вычисляются они не слева направо, а в строгом порядке:
- сначала
НЕ, - затем
И, - в последнюю очередь
ИЛИ.
Из-за этого запись без скобок часто означает не то, что задумано:
ГДЕ Организация = &Орг
И Статус = &Черновик ИЛИ Статус = &Отменён
Здесь И сильнее ИЛИ, поэтому условие читается как «(Организация И Черновик) ИЛИ Отменён» — и в результат попадут отменённые документы любых организаций. Чтобы получить ожидаемое «нужная организация И один из двух статусов», ставьте скобки:
ГДЕ Организация = &Орг
И (Статус = &Черновик ИЛИ Статус = &Отменён)
Привычка простая: как только в одном ГДЕ смешиваются И и ИЛИ, группу ИЛИ всегда берите в скобки. Лишние скобки читаемость не портят, а от логической ошибки страхуют.
Оператор НЕ удобно применять к В и ПОДОБНО: НЕ Номенклатура В ИЕРАРХИИ (&Услуги), НЕ Наименование ПОДОБНО "Тест%".
Отбор по ссылке и по полю составного типа
Ссылку сравнивают напрямую: ГДЕ Номенклатура = &Номенклатура. Чтобы захватить не один элемент, а целую группу справочника вместе со всем, что в ней лежит, используют В ИЕРАРХИИ:
ГДЕ Номенклатура В ИЕРАРХИИ (&ГруппаТоваров)
Сложнее с полями составного типа — такими, которые могут хранить ссылки на разные объекты. Классический пример — поле Регистратор регистра или Основание документа. Сравнивать его с конкретной ссылкой можно, а вот обратиться к реквизиту через точку без уточнения типа — нет: платформа не знает, у какого именно объекта брать реквизит.
Тип значения проверяют функциями ТИПЗНАЧЕНИЯ и ТИП:
ГДЕ ТИПЗНАЧЕНИЯ(Движения.Регистратор) = ТИП(Документ.РеализацияТоваровУслуг)
А чтобы добраться до реквизита объекта составного типа, значение приводят к нужному типу оператором ВЫРАЗИТЬ:
ВЫБРАТЬ
ВЫРАЗИТЬ(Движения.Регистратор КАК Документ.РеализацияТоваровУслуг).Контрагент КАК Контрагент
ИЗ
РегистрНакопления.Продажи КАК Движения
ГДЕ
ВЫРАЗИТЬ(Движения.Регистратор КАК Документ.РеализацияТоваровУслуг).Контрагент = &Контрагент
Если тип не тот, ВЫРАЗИТЬ вернёт NULL, а не ошибку, — такие строки просто не пройдут условие равенства. Это штатное поведение, на него можно опираться.
Отбор на пустую ссылку делают сравнением с пустым значением соответствующего типа: ГДЕ Контрагент <> ЗНАЧЕНИЕ(Справочник.Контрагенты.ПустаяСсылка). Оператор ЗНАЧЕНИЕ(...) подставляет предопределённое значение или пустую ссылку прямо в текст, без параметра.
Условие по данным из временной таблицы
Когда отбор идёт по большому списку (сотни и тысячи значений), передавать массив в В (&Массив) становится невыгодно — список раздувает текст запроса и мешает оптимизатору. Правильнее сложить значения во временную таблицу и фильтровать по ней.
Пакетный запрос с временной таблицей выглядит так:
ВЫБРАТЬ
Номенклатура.Ссылка КАК Ссылка
ПОМЕСТИТЬ ВТ_Товары
ИЗ
Справочник.Номенклатура КАК Номенклатура
ГДЕ
Номенклатура.ВидНоменклатуры = &Вид
;
ВЫБРАТЬ
Остатки.Номенклатура,
Остатки.КоличествоОстаток
ИЗ
РегистрНакопления.ТоварыНаСкладах.Остатки КАК Остатки
ГДЕ
Остатки.Номенклатура В (ВЫБРАТЬ Ссылка ИЗ ВТ_Товары)
Тот же отбор часто делают через ВНУТРЕННЕЕ СОЕДИНЕНИЕ с временной таблицей — соединение по ключу обычно отрабатывает быстрее, чем подзапрос В (...) на больших объёмах. Для этого перед работой с временной таблицей нужен менеджер временных таблиц:
МенеджерВТ = Новый МенеджерВременныхТаблиц;
Запрос.МенеджерВременныхТаблиц = МенеджерВТ;
Если по полю временной таблицы потом идёт соединение или В (...), объявите по нему индекс: ПОМЕСТИТЬ ВТ_Товары (Ссылка) ... ИНДЕКСИРОВАТЬ ПО Ссылка. На таблице в тысячи строк это заметно ускоряет второй запрос пакета.
Как ускорить отбор: индексы и условия в виртуальных таблицах
Медленный запрос — это чаще всего не «много данных», а условие, которое мешает использовать индекс. Несколько приёмов, которые реально влияют на скорость:
- Отбирайте по индексированным полям. У ссылки, даты документа, измерений регистра индексы есть по умолчанию. Условие по обычному реквизиту справочника без индекса заставит систему перебирать все строки.
- Не оборачивайте поле в функции.
ГДЕ ПОДСТРОКА(Код, 1, 3) = "100"илиГДЕ ГОД(Дата) = 2024— индекс по такому условию не работает . Перепишите через диапазон:Дата МЕЖДУ &Нач И &Кон. - Избегайте ведущего
%вПОДОБНО. Шаблон"кабель%"индекс использовать может, а"%кабель%"— уже нет, он приводит к полному сканированию. - Условие по регистру ставьте в параметры виртуальной таблицы, а не в
ГДЕ. Это самый частый промах. Фильтровать итоги нужно внутри виртуальной таблицы — тогда система отберёт данные ещё при расчёте, а не посчитает всё и отбросит лишнее:
ИЗ
РегистрНакопления.ТоварыНаСкладах.Остатки(
&НаДату,
Склад = &Склад И Номенклатура В ИЕРАРХИИ (&Группа)) КАК Остатки
Сравните с антипаттерном ИЗ ...Остатки(&НаДату, ) ... ГДЕ Склад = &Склад — во втором случае платформа сначала рассчитает остатки по всем складам и только потом выбросит ненужные.
ЛЕВОЕ СОЕДИНЕНИЕс проверкойГДЕ Поле ЕСТЬ NULLприменяйте осознанно — это рабочий приём «найти записи без пары», но на больших таблицах он тяжёлый.
Отдельно про NULL в условиях после соединений: если правая таблица не нашлась, её поля вернут NULL, и любое сравнение с ними (> 0, = &Значение) окажется ложным. Чтобы такие строки учитывались, оборачивайте поле в ЕСТЬNULL(Остаток, 0) — тогда вместо «неизвестно» подставится ноль, и условие ЕСТЬNULL(Остаток, 0) <= 0 корректно поймает позиции без остатка.
Частые ошибки в условиях запроса
=. Любое сравнение с NULL даёт «неизвестно», и строка не проходит. Используйте Поле ЕСТЬ NULL или оборачивайте в ЕСТЬNULL(Поле, 0).&Имя, для которого не вызвали УстановитьПараметр. Проверьте, что имена в тексте и в коде совпадают дословно, с учётом регистра.И и ИЛИ без скобок. Группу ИЛИ возьмите в круглые скобки — приоритет у И выше.ВЫРАЗИТЬ(Поле КАК Документ.ВидДокумента) или добавьте условие по ТИПЗНАЧЕНИЯ.Ещё один тонкий момент — строки. Язык запросов сравнивает строки как дополненные пробелами справа, поэтому "Петров" и "Петров " в условии = считаются равными. А вот в ПОДОБНО хвостовые пробелы уже значимы — об этом легко забыть при сверке с внешними данными.
Частые вопросы
Чем отличается условие в `ПО` от условия в `ГДЕ`?
ВНУТРЕННЕГО СОЕДИНЕНИЯ разницы в результате почти нет. А вот для ЛЕВОГО: условие в ПО отбирает строки правой таблицы до соединения (левая остаётся полной), а то же условие в ГДЕ отсекает строки уже после соединения — и фактически превращает левое соединение во внутреннее.Можно ли писать условие на английском (WHERE вместо ГДЕ)?
WHERE, AND, OR, SELECT. Но в типовых конфигурациях и справочниках принят русский вариант, смешивать их в одном запросе не стоит.Как отобрать строки, где поле попадает в один из нескольких диапазонов?
МЕЖДУ через ИЛИ в скобках: (Сумма МЕЖДУ 0 И 100) ИЛИ (Сумма МЕЖДУ 1000 И 5000). Границы в МЕЖДУ включаются с обеих сторон.Почему условие по реквизиту через точку (`Товар.Поставщик = &П`) замедляет запрос?
Номенклатура. На больших выборках это дороже, чем явное СОЕДИНЕНИЕ, которым соединение можно контролировать. Для разовых отборов это некритично.Как задать условие «значение не входит в список»?
НЕ перед В: ГДЕ НЕ Статус В (&Исключения). Учтите, что если в поле попадаются NULL, такие строки НЕ В (...) не вернёт — при необходимости добавьте ИЛИ Статус ЕСТЬ NULL.