При работе с языком запросов 1С, даже если вы используете полезную шпаргалку по языку запросов, можно столкнуться с неочевидной ситуацией: запрос с конструкцией ВЫБОР и группировкой по ней возвращает не только ожидаемые данные, но и дополнительную пустую строку со значением NULL. Попытки отфильтровать ее через условие ГДЕ ... ЕСТЬ НЕ NULL не приносят результата. Давайте проведем быстрый анализ данных, разберемся, почему так происходит, и какие существуют правильные способы решения этой задачи.
Проанализируем исходный запрос, который привел к проблеме. Для его отладки отлично подойдет консоль запросов УФ 8.3.2.24.22 (мод от Dr.Zombi). Задача — получить суммы из регистра накопления `ПрочиеРасходы` по определенному набору условий.
ВЫБРАТЬ
ВЫБОР
КОГДА ПрочиеРасходы.Подразделение В (&ПодразделениеМытКон)
И ПрочиеРасходы.СтатьяРасходов В (&СтатьяРасходовОсновнаяЗаработнаяПлата)
И ПрочиеРасходы.НастройкаХозяйственнойОперации В (&НастройкаХозяйственнойОперацииОтражЗарабПлат)
ТОГДА ПрочиеРасходы.Сумма
КОНЕЦ КАК РаспределениеНа20Счет
ИЗ
РегистрНакопления.ПрочиеРасходы КАК ПрочиеРасходы
ГДЕ
ПрочиеРасходы.Период = &Период
И НЕ ПрочиеРасходы.Сумма ЕСТЬ NULL
СГРУППИРОВАТЬ ПО
ВЫБОР
КОГДА ПрочиеРасходы.Подразделение В (&ПодразделениеМытКон)
И ПрочиеРасходы.СтатьяРасходов В (&СтатьяРасходовОсновнаяЗаработнаяПлата)
И ПрочиеРасходы.НастройкаХозяйственнойОперации В (&НастройкаХозяйственнойОперацииОтражЗарабПлат)
ТОГДА ПрочиеРасходы.Сумма
КОНЕЦ
Этот запрос возвращает две строки: одну с нужной суммой, а вторую — пустую, со значением NULL. Выясним причину такого поведения.
Корень проблемы кроется в совместном использовании конструкции ВЫБОР без секции ИНАЧЕ и последующей группировки СГРУППИРОВАТЬ ПО по этой же конструкции. Чтобы детально изучить поведение системы в таких случаях, можно использовать bm Консоль запросов, которая позволяет видеть промежуточные результаты.
Рассмотрим по шагам, как система обрабатывает этот запрос:
ПрочиеРасходы за указанный период (согласно секции ГДЕ).ВЫБОР.КОГДА выполняются, конструкция возвращает значение поля ПрочиеРасходы.Сумма.ВЫБОР, не имея секции ИНАЧЕ, по умолчанию возвращает значение NULL.СГРУППИРОВАТЬ ПО) система получает набор значений: несколько конкретных сумм и множество значений NULL. Механизм группировки объединяет все одинаковые значения. В итоге создается одна группа для каждой уникальной суммы и одна общая группа для всех строк, которые вернули NULL.Именно эта группа для всех NULL-значений и отображается в результате как лишняя пустая строка. Условие И НЕ ПрочиеРасходы.Сумма ЕСТЬ NULL в секции ГДЕ здесь не помогает, потому что оно отбирает только те строки, где изначально в поле Сумма был NULL. Оно никак не влияет на значения NULL, которые генерируются самой конструкцией ВЫБОР уже после отбора данных.
Теперь, когда причина ясна, рассмотрим несколько способов решения этой задачи, от самых простых до наиболее универсальных.
Если задача состоит в том, чтобы просто получить сумму по одному набору условий, то самый простой и производительный способ — это полностью отказаться от конструкции ВЫБОР в секциях ВЫБРАТЬ и СГРУППИРОВАТЬ ПО, перенеся все условия отбора в секцию ГДЕ.
В этом случае мы сначала отбираем только те строки, которые полностью удовлетворяют нашим критериям, а затем суммируем их значения.
ВЫБРАТЬ
СУММА(ПрочиеРасходы.Сумма) КАК РаспределениеНа20Счет
ИЗ
РегистрНакопления.ПрочиеРасходы КАК ПрочиеРасходы
ГДЕ
ПрочиеРасходы.Период = &Период
И ПрочиеРасходы.Подразделение В (&ПодразделениеМытКон)
И ПрочиеРасходы.СтатьяРасходов В (&СтатьяРасходовОсновнаяЗаработнаяПлата)
И ПрочиеРасходы.НастройкаХозяйственнойОперации В (&НастройкаХозяйственнойОперацииОтражЗарабПлат)
Преимущества:
Недостатки:
Это наиболее правильный и масштабируемый способ для задач, где нужно вывести несколько итоговых показателей в разных колонках. Идея заключается в том, чтобы не группировать по условному полю, а использовать агрегатную функцию (например, СУММА) и поместить конструкцию ВЫБОР внутрь нее.
Рассмотрим пример, как получить в одной строке суммы по зарплате и аренде для разных подразделений.
ВЫБРАТЬ
СУММА(ВЫБОР
КОГДА ПрочиеРасходы.СтатьяРасходов В (&СтатьяРасходовЗП)
И ПрочиеРасходы.Подразделение В (&ПодразделениеМытКон)
ТОГДА ПрочиеРасходы.Сумма
ИНАЧЕ 0
КОНЕЦ) КАК Зарплата_МытКон,
СУММА(ВЫБОР
КОГДА ПрочиеРасходы.СтатьяРасходов В (&СтатьяРасходовАренда)
И ПрочиеРасходы.Подразделение В (&ПодразделениеМытКон)
ТОГДА ПрочиеРасходы.Сумма
ИНАЧЕ 0
КОНЕЦ) КАК Аренда_МытКон,
СУММА(ВЫБОР
КОГДА ПрочиеРасходы.СтатьяРасходов В (&СтатьяРасходовЗП)
И ПрочиеРасходы.Подразделение В (&ДругоеПодразделение)
ТОГДА ПрочиеРасходы.Сумма
ИНАЧЕ 0
КОНЕЦ) КАК Зарплата_ДругоеПодразделение
ИЗ
РегистрНакопления.ПрочиеРасходы КАК ПрочиеРасходы
ГДЕ
ПрочиеРасходы.Период = &Период
Как это работает:
СГРУППИРОВАТЬ ПО, все записи схлопываются в одну итоговую строку.Зарплата_МытКон, Аренда_МытКон и т.д.) вычисляется своя сумма.СУММА() конструкция ВЫБОР проверяет каждую строку из регистра. Если строка подходит под условие, в суммирование идет ее значение Сумма. Если не подходит, то благодаря ИНАЧЕ 0 в суммирование идет 0, что не влияет на итоговый результат.Этот метод позволяет построить сложные отчеты с произвольным количеством колонок одним запросом, который вернет одну строку с нужными данными. Это эффективно и является стандартной практикой.
Если по какой-то причине необходимо сохранить исходную логику с группировкой по условному полю, отфильтровать итоговую группу с NULL можно с помощью секции ИМЕЮЩИЕ. Эта секция работает аналогично ГДЕ, но применяется не к исходным строкам таблицы, а к сгруппированным результатам. Кстати, подобная фильтрация часто обсуждается в контексте новых возможностей платформы, таких как добавление данных в существующую временную таблицу.
Доработаем исходный запрос:
ВЫБРАТЬ
ВЫБОР
КОГДА ... // Ваши условия
ТОГДА ПрочиеРасходы.Сумма
КОНЕЦ КАК РаспределениеНа20Счет
ИЗ
РегистрНакопления.ПрочиеРасходы КАК ПрочиеРасходы
ГДЕ
ПрочиеРасходы.Период = &Период
СГРУППИРОВАТЬ ПО
ВЫБОР
КОГДА ... // Те же самые условия
ТОГДА ПрочиеРасходы.Сумма
КОНЕЦ
ИМЕЮЩИЕ
РаспределениеНа20Счет ЕСТЬ НЕ NULL
Секция ИМЕЮЩИЕ РаспределениеНа20Счет ЕСТЬ НЕ NULL будет выполнена после того, как все данные сгруппированы, и просто отбросит ту группу, у которой итоговое значение поля РаспределениеНа20Счет оказалось равно NULL.
Важный момент: в секции ИМЕЮЩИЕ нужно ссылаться на псевдоним поля (РаспределениеНа20Счет), а не повторять всю конструкцию ВЫБОР.
Таким образом, мы рассмотрели несколько подходов к решению проблемы. Выбор конкретного метода зависит от конечной цели: для простого отбора данных идеально подходит перенос условий в ГДЕ, а для построения сложных отчетов с множеством показателей — условная агрегация является самым мощным и правильным инструментом.