К статье
Практический материал

Разработка и Конфигурация · 9 мин

Отличие ГДЕ от ИМЕЮЩИЕ в запросах 1С

Чем отличается ГДЕ от ИМЕЮЩИЕ в запросах 1С: порядок выполнения, работа с группировкой и итогами, примеры кода, типичные ошибки и советы по оптимизации.

В языке запросов 1С условия отбора можно задать двумя способами: секцией ГДЕ и секцией ИМЕЮЩИЕ (аналог HAVING в классическом SQL). На первый взгляд они делают одно и то же — фильтруют записи. Но работают эти конструкции на разных этапах выполнения запроса, и неправильный выбор приводит либо к ошибке «Поле не найдено», либо к неверному результату, либо к заметному падению производительности.

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

Ниже разберём порядок выполнения запроса, синтаксис обеих секций, практические примеры, влияние на скорость и типичные ошибки разработчиков.

Порядок выполнения запроса: почему это важно

Чтобы понять разницу, нужно представить логическую последовательность обработки запроса. Сначала система выбирает данные из таблиц (секция ИЗ), соединяет их, затем применяет отбор ГДЕ — отбрасываются все строки, не подходящие под условие. Только после этого выполняется группировка (СГРУППИРОВАТЬ ПО) и вычисляются агрегатные функции. И уже в самом конце к полученным группам применяется ИМЕЮЩИЕ.

Из этого порядка следуют два практических вывода. Первый: чем больше строк отсечено на этапе ГДЕ, тем меньше данных придётся группировать — это напрямую влияет на скорость. Второй: в секции ИМЕЮЩИЕ можно ссылаться только на поля группировки и агрегатные функции, потому что детальные строки к этому моменту уже «свёрнуты» в итоги.

Секция ГДЕ: синтаксис и когда применять

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

ВЫБРАТЬ

Продажи.Контрагент,

СУММА(Продажи.СуммаОборот) КАК СуммаПродаж

ИЗ

РегистрНакопления.Продажи.Обороты(&НачалоПериода, &КонецПериода, , ) КАК Продажи

ГДЕ

Продажи.Контрагент В (&СписокКонтрагентов)

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

Продажи.Контрагент

В этом примере отбор по контрагентам выполняется до группировки: в агрегацию попадут только нужные строки. Это правильный и быстрый вариант. Тот же отбор через ИМЕЮЩИЕ сработал бы, но заставил бы систему сначала сгруппировать всех контрагентов, а потом выбросить лишние группы — лишняя работа на больших таблицах.

Секция ИМЕЮЩИЕ: синтаксис и типовые сценарии

ИМЕЮЩИЕ пишется после СГРУППИРОВАТЬ ПО и содержит условия на агрегатные функции и поля группировки. Классический сценарий — отбор по посчитанному итогу: клиенты с суммой покупок выше порога, номенклатура с нулевым остатком, сотрудники с количеством документов больше N.

ВЫБРАТЬ

Продажи.Контрагент,

СУММА(Продажи.СуммаОборот) КАК СуммаПродаж

ИЗ

РегистрНакопления.Продажи.Обороты(&НачалоПериода, &КонецПериода, , ) КАК Продажи

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

Продажи.Контрагент

ИМЕЮЩИЕ

СУММА(Продажи.СуммаОборот) > 100000

Обратите внимание: если попытаться перенести условие СУММА(...) > 100000 в секцию ГДЕ, конструктор запроса выдаст ошибку — агрегатные функции там недопустимы. Это и есть главный критерий выбора: условие на итог — только ИМЕЮЩИЕ.

ИМЕЮЩИЕ без явной группировки

В языке запросов 1С секцию ИМЕЮЩИЕ можно использовать и без явного СГРУППИРОВАТЬ ПО, если в выборке присутствуют агрегатные функции. В этом случае весь результат считается одной группой, и условие либо оставляет её, либо отсекает целиком. На практике такой приём встречается редко, но полезен, например, для проверки «есть ли хотя бы один документ за период» через КОЛИЧЕСТВО.

Условие на поле группировки в ИМЕЮЩИЕ

Технически в ИМЕЮЩИЕ можно написать условие вида ИМЕЮЩИЕ Продажи.Контрагент = &Контрагент, если Контрагент — поле группировки. Запрос выполнится, но это антипаттерн: фильтрация произойдёт после агрегации, то есть поздно и неэффективно. Такие условия всегда следует переносить в ГДЕ.

ГДЕ или ИМЕЮЩИЕ

ГДЕ

Условия по полям таблиц: контрагент, период, номенклатура, признаки. Применяется до группировки, быстрее, поддерживает индексы. Не принимает агрегатные функции.

ИМЕЮЩИЕ

Условия по итогам групп: СУММА, КОЛИЧЕСТВО, МАКСИМУМ, МИНИМУМ, СРЕДНЕЕ. Применяется после группировки. Единственный вариант для фильтра по агрегатам.

Сравнение на практическом примере

Рассмотрим задачу: найти контрагентов из заданного списка, чья сумма продаж за период превысила 100 000. Здесь два условия разной природы, и правильное решение — разнести их по секциям:

ВЫБРАТЬ

Продажи.Контрагент,

СУММА(Продажи.СуммаОборот) КАК СуммаПродаж

ИЗ

РегистрНакопления.Продажи.Обороты(&НачалоПериода, &КонецПериода, , ) КАК Продажи

ГДЕ

Продажи.Контрагент В (&СписокКонтрагентов)

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

Продажи.Контрагент

ИМЕЮЩИЕ

СУММА(Продажи.СуммаОборот) > 100000

Логика разделения: отбор по контрагентам — детальное условие, оно уменьшает объём данных до группировки, поэтому живёт в ГДЕ. Условие по сумме — проверка уже посчитанного итога, поэтому оно в ИМЕЮЩИЕ. Такой запрос и читается лучше, и выполняется оптимальнее.

Как выбрать секцию для условия
  1. 1
    Определите, к чему относится условие: к полю исходной таблицы или к итогу группы.
  2. 2
    Если к полю таблицы — запишите условие в ГДЕ (или в параметры виртуальной таблицы).
  3. 3
    Если к агрегатной функции (СУММА, КОЛИЧЕСТВО и др.) — запишите в ИМЕЮЩИЕ после СГРУППИРОВАТЬ ПО.
  4. 4
    Проверьте запрос в консоли на реальных данных: сравните количество строк и время выполнения.

Производительность и оптимизация

Разница в скорости становится заметной на больших регистрах. Условие в ГДЕ отсекает строки до ресурсоёмкой операции группировки, и СУБД может использовать индексы. ИМЕЮЩИЕ работает с уже материализованным результатом агрегации — индексы на этом этапе не помогают, а отсечённые группы всё равно были посчитаны.

Практическое правило: всё, что можно отфильтровать без агрегатов, должно фильтроваться в ГДЕ или в параметрах виртуальной таблицы. ИМЕЮЩИЕ оставляйте только для условий, которые принципиально требуют посчитанных итогов. При сомнениях замерьте запрос на копии базы с реальным объёмом данных — разница на миллионах движений может быть кратной.

Частая ситуация: разработчик переносит запрос из чистого SQL и по привычке пишет HAVING для всех условий. В 1С конструктор запросов сам подскажет ошибку при проверке, но лучше сразу раскладывать условия по секциям — это упрощает и чтение, и оптимизацию.
ПЗ
Практическое замечание

Типичные ошибки

Самая распространённая ошибка — попытка использовать агрегатную функцию в ГДЕ. Конструктор запросов и синтакс-помощник сразу сообщают, что агрегатные функции в этой секции недопустимы. Решение одно: перенести условие в ИМЕЮЩИЕ и убедиться, что поле участвует в группировке.

Вторая ошибка — обратная: условия по обычным полям размещают в ИМЕЮЩИЕ. Запрос выполняется, результат формально верный, но на больших данных наблюдается лишняя нагрузка: группируются тысячи строк, которые потом отбрасываются. Третья типичная проблема — ссылка в ИМЕЮЩИЕ на поле, которое не входит ни в группировку, ни в агрегат. Такое поле после свёртки не существует, и запрос упадёт с ошибкой «Поле не найдено».

Типичные ошибки
01
Агрегатная функция в ГДЕ — ошибка компиляции запроса; условие нужно перенести в ИМЕЮЩИЕ.
02
Отбор по обычному полю в ИМЕЮЩИЕ — запрос работает, но медленнее: группируются лишние строки.
03
Ссылка в ИМЕЮЩИЕ на поле вне группировки и агрегатов — ошибка «Поле не найдено».
04
Проверка результата только на маленькой тестовой базе — проблемы производительности не видны до внедрения.
Контрольные точки

Как проверить результат

Расположение условий
Норма

Поля таблиц — в ГДЕ, агрегаты — в ИМЕЮЩИЕ

Тревожный признак

Агрегаты в ГДЕ или детальные условия в ИМЕЮЩИЕ

Выполнение запроса
Норма

Запрос выполняется без ошибок, число строк ожидаемое

Тревожный признак

Ошибка «Поле не найдено» или пустой результат

Скорость на реальных данных
Норма

Время выполнения приемлемое на рабочем объёме

Тревожный признак

Запрос «висит» только на большой базе

FAQ

Можно ли использовать ГДЕ и ИМЕЮЩИЕ в одном запросе?

Да, и это нормальная практика. ГДЕ отбирает строки до группировки, ИМЕЮЩИЕ — группы после неё. Разнесение условий по секциям делает запрос и быстрее, и понятнее.

Работает ли ИМЕЮЩИЕ без СГРУППИРОВАТЬ ПО?

Да, если в выборке есть агрегатные функции. Весь результат трактуется как одна группа, и условие либо возвращает её, либо даёт пустой результат. Без агрегатов такая конструкция бессмысленна.

Почему запрос с условием СУММА в ГДЕ выдаёт ошибку?

Потому что ГДЕ обрабатывается до группировки, когда агрегатные значения ещё не вычислены. Язык запросов запрещает агрегатные функции в этой секции — условие нужно записать в ИМЕЮЩИЕ.

Что быстрее: отбор в параметрах виртуальной таблицы или в ГДЕ?

Обычно отбор в параметрах виртуальной таблицы эффективнее: фильтр применяется на уровне построения виртуальной таблицы и лучше использует индексы. ГДЕ — следующий по приоритету вариант, ИМЕЮЩИЕ — последний.

Можно ли в ИМЕЮЩИЕ использовать псевдоним поля, например СуммаПродаж?

В языке запросов 1С в секции ИМЕЮЩИЕ надёжнее повторять агрегатное выражение целиком — СУММА(Продажи.СуммаОборот). Поведение с псевдонимами зависит от версии платформы, поэтому явное выражение — более предсказуемый вариант.

Итог

Что важно запомнить

ГДЕ фильтрует строки до группировки и не принимает агрегатные функции; ИМЕЮЩИЕ фильтрует группы после агрегации и нужен именно для условий по СУММА, КОЛИЧЕСТВО и другим итогам. Всё, что можно отобрать без агрегатов, выносите в ГДЕ или в параметры виртуальной таблицы — так запрос работает быстрее и читается легче.