Разработка и Конфигурация · 9 мин
Чем отличается ГДЕ от ИМЕЮЩИЕ в запросах 1С
Разбираем разницу между ГДЕ и ИМЕЮЩИЕ в языке запросов 1С: синтаксис, примеры, типичные ошибки и правила выбора условия для группировок и обычных отборов.
И ГДЕ, и ИМЕЮЩИЕ в языке запросов 1С задают условия отбора, но работают на разных этапах выполнения запроса. ГДЕ фильтрует строки до группировки — то есть отбирает исходные записи таблицы. ИМЕЮЩИЕ применяется после группировки и фильтрует уже сгруппированные результаты по значениям агрегатных функций (СУММА, КОЛИЧЕСТВО, МАКСИМУМ и других).
Путаница возникает из-за того, что оба оператора выглядят похоже и оба «отсекают лишнее». На практике разница принципиальна: если попытаться отфильтровать по сумме через ГДЕ, запрос либо вернёт ошибку, либо даст неверный результат, а если профильтровать обычные поля через ИМЕЮЩИЕ — запрос будет работать медленнее, чем мог бы.
Ниже разберём синтаксис обоих операторов, покажем примеры на типовых задачах (остатки, обороты, дубли), разберём ошибки и дадим простое правило выбора.
Что делает оператор ГДЕ
ГДЕ — это условие отбора строк источника данных. Оно применяется к каждой записи таблицы (или виртуальной таблицы) ещё до того, как выполняется группировка и вычисляются итоги. В условии можно использовать поля таблиц, параметры, операции сравнения, ПОДОБНО, В, МЕЖДУ, проверку на NULL и логические связки И/ИЛИ.
Типичный пример — отбор оборотов по конкретной организации и периоду:
Выбрать
Продажи.Номенклатура,
Сумма(Продажи.СуммаОборот) Как Сумма
Из
РегистрНакопления.Продажи.Обороты Как Продажи
Где
Продажи.Номенклатура.ВидНоменклатуры = &ВидНоменклатуры
Сгруппировать По
Продажи.Номенклатура
Здесь ГДЕ отсекает лишние строки до группировки. Это важно и для производительности: чем раньше отфильтрованы данные, тем меньше записей участвует в группировке и соединениях.
Ключевое ограничение: в ГДЕ нельзя использовать агрегатные функции. Конструкция ГДЕ Сумма(Продажи.СуммаОборот) > 1000 вызовет ошибку, потому что на момент применения ГДЕ суммы ещё не вычислены.
Что делает оператор ИМЕЮЩИЕ
ИМЕЮЩИЕ применяется после того, как выполнена группировка (СГРУППИРОВАТЬ ПО) и посчитаны агрегатные функции. Он отбирает уже сформированные группы по их итоговым значениям. Это прямой аналог HAVING в SQL, и в запросах 1С можно писать даже английское ключевое слово HAVING — оно является синонимом.
Классическая задача — найти товары, сумма продаж которых превышает порог:
Выбрать
Продажи.Номенклатура,
Сумма(Продажи.СуммаОборот) Как Сумма
Из
РегистрНакопления.Продажи.Обороты Как Продажи
Сгруппировать По
Продажи.Номенклатура
Имеющие
Сумма(Продажи.СуммаОборот) > 100000
Без ИМЕЮЩИЕ такую задачу пришлось бы решать в два этапа: сначала сгруппировать данные во временную таблицу, затем наложить ГДЕ на её результат. ИМЕЮЩИЕ делает то же самое в одном запросе.
В условии ИМЕЮЩИЕ допустимы агрегатные функции, поля группировки и их комбинации, а также параметры и логические операторы — как и в ГДЕ.
Ключевые отличия ГДЕ и ИМЕЮЩИЕ
Различия удобно свести в таблицу — по ней легко проверить себя при написании запроса.
| Критерий | ГДЕ | ИМЕЮЩИЕ |
|---|---|---|
| Этап применения | До группировки | После группировки |
| Что фильтрует | Строки исходных таблиц | Сгруппированные итоги |
| Агрегатные функции | Запрещены | Разрешены |
| Требует СГРУППИРОВАТЬ ПО | Нет | Да, работает с группировкой |
| Влияние на скорость | Сокращает объём данных на раннем этапе | Фильтрует уже обработанные группы |
Практическое следствие: если условие можно выразить через ГДЕ — так и делайте. Фильтрация на раннем этапе уменьшает количество записей, которые придётся группировать и соединять, и запрос выполнится быстрее.
Сравнение вариантов
Подходит для отбора по обычным полям: организация, период, номенклатура, признаки. Не работает с итогами группировки.
Подходит для отбора по итогам: сумма больше порога, количество записей больше одного. Требует группировки и работает медленнее, чем ГДЕ на том же объёме.
Примеры типовых задач
Поиск дублей в справочнике
Найти элементы справочника с одинаковыми наименованиями можно только через ИМЕЮЩИЕ, потому что условие накладывается на количество записей в группе:
Выбрать
Контрагенты.Наименование,
Количество(Контрагенты.Ссылка) Как КоличествоДублей
Из
Справочник.Контрагенты Как Контрагенты
Сгруппировать По
Контрагенты.Наименование
Имеющие
Количество(Контрагенты.Ссылка) > 1
Отбор по обычному полю и по итогу одновременно
Оба оператора можно сочетать в одном запросе. Например, показать клиентов конкретного региона, у которых сумма продаж превысила порог:
Выбрать
Продажи.Контрагент,
Сумма(Продажи.СуммаОборот) Как Сумма
Из
РегистрНакопления.Продажи.Обороты Как Продажи
Где
Продажи.Контрагент.Регион = &Регион
Сгруппировать По
Продажи.Контрагент
Имеющие
Сумма(Продажи.СуммаОборот) > &Порог
Здесь ГДЕ отсекает чужие регионы до группировки, а ИМЕЮЩИЕ убирает группы с маленькими итогами. Каждый оператор делает свою работу на своём этапе.
- 1Сформулируйте условие словами: что именно отсекаем — отдельные строки или итоги по группам.
- 2Если в условии есть СУММА, КОЛИЧЕСТВО, МАКСИМУМ, МИНИМУМ, СРЕДНЕЕ — используйте ИМЕЮЩИЕ.
- 3Если условие накладывается на обычные поля таблицы — используйте ГДЕ.
- 4Если нужны оба вида условий — напишите ГДЕ до группировки, ИМЕЮЩИЕ после.
- 5Проверьте результат на тестовых данных: количество строк и итоги должны совпадать с ожиданием.
Типичные ошибки и ограничения
Самая частая ошибка — попытка написать агрегатную функцию в ГДЕ. Запрос не выполнится, конструктор выдаст сообщение об ошибке вроде «Агрегатная функция не может использоваться в условии». Лечится переносом условия в ИМЕЮЩИЕ.
Вторая ошибка — обратная: разработчик пишет условие по обычному полю в ИМЕЮЩИЕ, потому что «так тоже работает». Работает, но медленнее: сначала группируются все строки, и только потом лишние группы отбрасываются. На больших таблицах разница во времени выполнения заметна.
Третий подводный камень — ИМЕЮЩИЕ без явной группировки. Если в запросе есть агрегатные функции, группировка подразумевается по всему результату (одна итоговая строка), и ИМЕЮЩИЕ будет фильтровать именно её. Это редко то, что нужно, поэтому всегда явно проверяйте секцию СГРУППИРОВАТЬ ПО.
Влияние на производительность
ГДЕ выполняется до группировки и соединений, поэтому правильно построенное условие резко сокращает объём обрабатываемых данных. Особенно это важно при работе с виртуальными таблицами регистров: параметры виртуальной таблицы (период, отбор) ещё эффективнее, чем ГДЕ, потому что применяются на уровне построения самой виртуальной таблицы.
ИМЕЮЩИЕ неизбежно обрабатывает полный набор групп. Если запрос медленный и в нём есть ИМЕЮЩИЕ, проверьте, нельзя ли часть условий перенести в ГДЕ или в параметры виртуальной таблицы. Полностью отказаться от ИМЕЮЩИЕ при отборе по итогам нельзя — но можно уменьшить объём данных до группировки.
Как проверить результат
Нет сообщений про агрегатные функции в условии
Ошибка «Агрегатная функция не может использоваться в условии» — функция попала в ГДЕ
Совпадает с ручным подсчётом на тестовых данных
Строк больше или меньше ожидаемого — проверьте, не в ИМЕЮЩИЕ ли условие по обычному полю
Условия по полям — в ГДЕ или в параметрах виртуальной таблицы
Все условия собраны в ИМЕЮЩИЕ — группируется лишний объём
FAQ
Можно ли использовать ИМЕЮЩИЕ без ГДЕ?
Да, если условий по исходным строкам нет. Например, при поиске дублей достаточно СГРУППИРОВАТЬ ПО и ИМЕЮЩИЕ — секцию ГДЕ можно опустить полностью.
Можно ли в ИМЕЮЩИЕ ссылаться на псевдоним поля из секции ВЫБРАТЬ?
Надёжнее повторять саму агрегатную функцию: Сумма(Продажи.СуммаОборот) > 1000. Поведение при использовании псевдонимов в ИМЕЮЩИЕ зависит от версии платформы, а запись с явной функцией работает всегда.
Чем ИМЕЮЩИЕ отличается от отбора во временной таблице?
По результату — ничем: можно сгруппировать данные во временную таблицу, а затем наложить ГДЕ на её поля. ИМЕЮЩИЕ делает то же самое компактнее, в одном запросе, без создания промежуточной таблицы.
Работает ли ГДЕ с итогами по иерархии (ИТОГИ ПО)?
Нет. Секция ИТОГИ ПО формирует строки итогов уже после выполнения запроса, и ни ГДЕ, ни ИМЕЮЩИЕ на них не влияют. Отфильтровать итоговые строки запросом нельзя — только на уровне обработки результата.
Почему условие в ИМЕЮЩИЕ с параметром иногда не отбирает записи?
Частая причина — NULL в результате агрегатной функции (например, СУММА по пустому набору даёт NULL). Сравнение NULL с числом всегда ложно. Оберните функцию в ЕСТЬNULL: ЕстьNull(Сумма(...), 0) > &Порог.
Что важно запомнить
ГДЕ фильтрует строки до группировки и не принимает агрегатные функции. ИМЕЮЩИЕ фильтрует группы после вычисления итогов и предназначен именно для условий по СУММА, КОЛИЧЕСТВО и другим агрегатам. Условия по обычным полям всегда выносите в ГДЕ или в параметры виртуальных таблиц — так запрос работает быстрее.