При работе с Конструктором запросов в 1С:Предприятии разработчики часто сталкиваются с ситуацией, когда платформа автоматически добавляет выбранные поля в секцию СГРУППИРОВАТЬ ПО. Попытка вручную удалить «лишнее» поле из группировки в текстовом редакторе приводит к синтаксической ошибке при сохранении или выполнении запроса: «Поле не входит в группу...».
В этой статье мы подробно разберем, почему СУБД и платформа 1С ведут себя именно так, проанализируем базовое правило агрегации данных и рассмотрим четыре практических способа решить проблему автора темы, когда поле Состояние мешает корректному суммированию остатков по оборудованию.
Прежде чем перейти к написанию кода, давайте разберем теорию. В языке запросов 1С (как и в стандартном SQL) действует строгое математическое правило:
Все поля, перечисленные в секции ВЫБРАТЬ, должны быть либо сгруппированы (указаны в секции СГРУППИРОВАТЬ ПО), либо обернуты в одну из агрегатных функций (СУММА, МИНИМУМ, МАКСИМУМ, СРЕДНЕЕ, КОЛИЧЕСТВО).
Представим, что у нас есть три записи в базе данных:
Если мы хотим получить сумму остатков (СУММА(Количество)), сгруппировав результат только по полю Оборудование, СУБД легко выполнит эту операцию: она сложит все количества и выдаст одну строку: "Принтер HP" — 10 шт.
Но что произойдет, если мы потребуем вывести в результат еще и поле Состояние, отказавшись при этом группировать по нему? Система окажется в тупике. Она рассчитала общую сумму (10 шт.) для одной строки "Принтер HP", но какое именно состояние ей вывести напротив этого значения? "Новый", "Б/У" или "В ремонте"? СУБД не может принять это решение самостоятельно. Именно поэтому Конструктор запросов 1С автоматически страхует разработчика от неопределенности и добавляет поле Состояние в группировку, что в итоге разделяет (детализирует) наши остатки обратно на 3 строки.
Давайте по шагам разберем, как обойти эту проблему и получить требуемый результат в зависимости от ваших задач.
Проанализируем ситуацию: если в конечном отчете или алгоритме поле Состояние вам вообще не требуется, а нужны лишь чистые суммовые остатки по оборудованию, лучшим решением будет просто удалить это поле из списка выбора.
Рассмотрим, как будет выглядеть корректный текст запроса в таком случае:
ВЫБРАТЬ
МестонахожденияОстатки.Оборудование КАК Оборудование,
СУММА(МестонахожденияОстатки.КоличествоОстаток) КАК Остаток
ИЗ
РегистрНакопления.Местонахождения.Остатки КАК МестонахожденияОстатки
СГРУППИРОВАТЬ ПО
МестонахожденияОстатки.Оборудование
При таком подходе СУБД без проблем сложит все остатки по каждому уникальному оборудованию, проигнорировав различия в их состояниях. На выходе мы получим компактную и точную таблицу.
Бывают задачи, когда поле Состояние обязательно должно присутствовать в результирующем наборе данных (например, этого требует структура принимающей таблицы или интерфейс), но при этом группировать (размножать строки) по нему по-прежнему нельзя.
В этом случае мы можем применить к текстовому или ссылочному полю Состояние агрегатные функции МИНИМУМ или МАКСИМУМ. Давайте посмотрим на пример такого запроса:
ВЫБРАТЬ
МестонахожденияОстатки.Оборудование КАК Оборудование,
МИНИМУМ(МестонахожденияОстатки.Состояние) КАК Состояние,
СУММА(МестонахожденияОстатки.КоличествоОстаток) КАК Остаток
ИЗ
РегистрНакопления.Местонахождения.Остатки КАК МестонахожденияОстатки
СГРУППИРОВАТЬ ПО
МестонахожденияОстатки.Оборудование
Разберем, как отработает этот алгоритм. СУБД сгруппирует данные строго по оборудованию и посчитает общую сумму остатков. В колонку Состояние запишется ровно одно значение, которое окажется минимальным (например, по алфавитному порядку среди всех состояний данного оборудования). Строки двоиться и троиться не будут.
Если вы разрабатываете отчет на базе СКД, то жестко прописывать секцию СГРУППИРОВАТЬ ПО в самом SQL-запросе очень часто является грубой методологической ошибкой. СКД — это мощный интеллектуальный процессор, который умеет трансформировать запросы на лету.
Давайте выполним задачу по правилам разработки в СКД:
ВЫБРАТЬ
МестонахожденияОстатки.Оборудование КАК Оборудование,
МестонахожденияОстатки.Состояние КАК Состояние,
МестонахожденияОстатки.КоличествоОстаток КАК КоличествоОстаток
ИЗ
РегистрНакопления.Местонахождения.Остатки КАК МестонахожденияОстатки
КоличествоОстаток в правое окно ресурсов, определив для него выражение СУММА(КоличествоОстаток).Оборудование.Проанализируем результат: компоновщик данных автоматически поймет структуру настроек и сам сформирует оптимальный SQL-запрос к СУБД. На экране пользователь увидит свернутые остатки по оборудованию без учета состояний. Если же завтра пользователю понадобится увидеть детализацию по состояниям, ему достаточно будет просто добавить подчиненную группировку в настройках отчета, не меняя при этом код запроса!
Еще один классический способ решения подобных аналитических задач в 1С — это использование конструкции ИТОГИ ПО вместо жесткой группировки. Итоги позволяют получить древовидную, иерархическую структуру данных прямо из базы.
Давайте посмотрим на практический пример реализации:
ВЫБРАТЬ
МестонахожденияОстатки.Оборудование КАК Оборудование,
МестонахожденияОстатки.Состояние КАК Состояние,
МестонахожденияОстатки.КоличествоОстаток КАК КоличествоОстаток
ИЗ
РегистрНакопления.Местонахождения.Остатки КАК МестонахожденияОстатки
ИТОГИ
СУММА(КоличествоОстаток)
ПО
Оборудование
В результате выполнения такого запроса мы получим двухуровневую выборку:
Для повышения профессионального уровня работы с запросами в 1С, рассмотрим две полезные особенности платформы:
1. Автоматическая группировка реквизитов «через точку»
В платформе 1С реализован эффективный механизм оптимизации SQL-кода. Если вы группируете данные по ссылочному полю (например, Оборудование), то любые его реквизиты, выбранные через точку в секции ВЫБРАТЬ, указывать в секции СГРУППИРОВАТЬ ПО не требуется!
Посмотрим на пример правильного кода:
ВЫБРАТЬ
МестонахожденияОстатки.Оборудование КАК Оборудование,
МестонахожденияОстатки.Оборудование.Код КАК КодОборудования,
МестонахожденияОстатки.Оборудование.СтатьяНалоговогоУчета КАК СтатьяУчета,
СУММА(МестонахожденияОстатки.КоличествоОстаток) КАК Остаток
ИЗ
РегистрНакопления.Местонахождения.Остатки КАК МестонахожденияОстатки
СГРУППИРОВАТЬ ПО
МестонахожденияОстатки.Оборудование
Платформа самостоятельно сгруппирует реквизиты Код и СтатьяНалоговогоУчета, что значительно упрощает чтение и поддержку кода разработчиками.
2. Использование конструкции ГРУППИРУЮЩИМ НАБОРАМ (с версии 8.3.16)
Если вам необходимо в рамках одного запроса быстро рассчитать разные варианты и комбинации группировок (например, отдельно получить итоги по оборудованию, отдельно — по состояниям, и дополнительно совместный вариант), используйте конструкцию ГРУППИРУЮЩИМ НАБОРАМ. Это работает намного быстрее, чем соединение нескольких тяжелых запросов через оператор ОБЪЕДИНИТЬ ВСЕ.