Почему Конструктор запроса автоматически добавляет поля в группировку и как с этим работать?

Программист 1С v8.3 (Обычные формы) 1С:Зарплата и Управление Персоналом Налоговый учет IT и автоматизация бизнеса
← На главную

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

Фундаментальное правило использования агрегатных функций

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

Давайте выясним причину этого ограничения. Когда мы просим систему рассчитать, например, СУММА(Взнос), база данных должна «схлопнуть» несколько строк в одну. Чтобы это произошло корректно, системе нужно указать «координаты» этого суммирования — то есть те разрезы (измерения), в рамках которых значения будут складываться. Если мы выбираем поле ФизЛицо и при этом считаем сумму, то СУБД должна понимать: мы хотим видеть общую сумму по каждому физическому лицу в отдельности.

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

Разбор сложной ситуации на примере кода

Рассмотрим ситуацию, с которой столкнулся автор темы. В его запросе используется множество соединений и сложных условий ВЫБОР. Проанализируем фрагмент этого кода, чтобы понять масштаб задачи:


ВЫБРАТЬ РАЗРЕШЕННЫЕ
    ОПВРасчетыСФондами.ФизЛицо КАК ФизЛицо,
    ВЫБОР
        КОГДА ОПВРасчетыСФондами.ПодразделениеОрганизации <> ЗНАЧЕНИЕ(Справочник.ПодразделенияОрганизаций.ПустаяСсылка)
            ТОГДА ОПВРасчетыСФондами.ПодразделениеОрганизации
        ИНАЧЕ РаботникиОрганизации.ПодразделениеОрганизации
    КОНЕЦ КАК ПодразделениеОрганизации,
    ОПВРасчетыСФондами.ВидПлатежа КАК ВидПлатежа,
    СУММА(ОПВРасчетыСФондами.Взнос) КАК Взнос,
    // ... далее идут еще десятки полей с ВЫБОР ...
ИЗ
    РегистрНакопления.ОПВРасчетыСФондами КАК ОПВРасчетыСФондами
    // ... здесь соединения ...
ГДЕ
    // ... здесь условия ...
СГРУППИРОВАТЬ ПО
    ОПВРасчетыСФондами.ФизЛицо,
    ВЫБОР
        КОГДА ОПВРасчетыСФондами.ПодразделениеОрганизации <> ЗНАЧЕНИЕ(Справочник.ПодразделенияОрганизаций.ПустаяСсылка)
            ТОГДА ОПВРасчетыСФондами.ПодразделениеОрганизации
        ИНАЧЕ РаботникиОрганизации.ПодразделениеОрганизации
    КОНЕЦ,
    ОПВРасчетыСФондами.ВидПлатежа
    // ... и так далее для каждого выражения ...

Как мы видим, поле СУММА(ОПВРасчетыСФондами.Взнос) является агрегируемым. Следовательно, все остальные конструкции, включая длинные блоки ВЫБОР...КОГДА, обязаны дублироваться в секции СГРУППИРОВАТЬ ПО. Важный нюанс: в группировку нельзя подставить псевдоним (алиас) поля, например КАК ПодразделениеОрганизации. Необходимо копировать всё выражение целиком. Это делает текст запроса громоздким, но это единственный способ заставить его работать.

Особенности работы с полями «через точку»

Проанализируем ещё одну тонкость, упомянутую в обсуждении. Существует мнение, что если поле уже сгруппировано, то его реквизиты, полученные «через точку», группировать не обязательно. С точки зрения чистого SQL это часто так, но в 1С есть свои особенности.

  1. Если вы используете в выборке поле Номенклатура.Код и при этом в группировке стоит сама Номенклатура (ссылка), Конструктор запроса все равно может добавить Номенклатура.Код в секцию группировки.
  2. Это происходит потому, что 1С стремится к максимальной совместимости с различными СУБД (MS SQL Server, PostgreSQL, Oracle, IBM DB2), у которых правила работы с зависимыми полями могут различаться.
  3. Практический совет: если вам нужно минимизировать количество полей в группировке для повышения производительности, старайтесь сначала выполнить группировку по ссылкам во временной таблице, а уже затем в следующем пакете запроса присоединять необходимые реквизиты (наименования, артикулы и т.д.).

Как избежать автоматического добавления в группировку?

Если вы категорически не хотите видеть поле в секции СГРУППИРОВАТЬ ПО, у вас есть только два пути:

Путь первый: Использование агрегатной функции. Если поле содержит текстовую информацию или ссылку, и вы знаете, что для данной группы значение всегда будет одинаковым (или вам неважно, какое именно из нескольких значений будет выбрано), используйте функции МАКСИМУМ или МИНИМУМ. Например, МАКСИМУМ(ОПВРасчетыСФондами.ФизЛицо). В этом случае поле перестает требовать группировки, так как оно уже «обработано» агрегатом.

Путь второй: Отказ от группировки. Если вам нужно просто избавиться от повторяющихся строк, но при этом вы не используете суммирование, вместо СГРУППИРОВАТЬ ПО следует использовать ключевое слово РАЗЛИЧНЫЕ (ВЫБРАТЬ РАЗЛИЧНЫЕ ...). Однако помните, что РАЗЛИЧНЫЕ работает медленнее на больших объемах данных и не позволяет использовать СУММА.

Рекомендации по оптимизации запросов с группировками

Проанализировав ситуацию, мы можем составить алгоритм эффективной работы с группировками:

Таким образом, автоматическое поведение Конструктора запроса в 1С — это не ошибка, а механизм защиты от неверных SQL-запросов. Понимая правила взаимодействия агрегатных функций и группировок, вы сможете писать более чистый, эффективный и предсказуемый код.

← На главную