В практике разработки на платформе 1С:Предприятие часто возникает задача фильтрации данных по количеству подчиненных элементов. Типичный пример: необходимо найти контрагентов, у которых все объекты обслуживания находятся в одном и том же статусе (например, только «Активен» или только «Приостановлен»), и при этом вывести этот самый статус в результат запроса.
На первый взгляд задача кажется простой, однако новички часто сталкиваются с логической ловушкой при попытке использовать группировку. Давайте подробно разберем, почему стандартный подход может не работать, и рассмотрим наиболее эффективные способы решения этой задачи.
Проанализируем ситуацию, представленную в исходном вопросе. Автор пытался использовать следующий код:
ВЫБРАТЬ
ОБ_ох.Контрагенты КАК Контрагенты,
ВЫБОР
КОГДА КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ОБ_ох.StatusОбъекта) = 1
ТОГДА ОБ_ох.StatusОбъекта
КОНЕЦ КАК Поле1
ИЗ
ОБ_ох КАК ОБ_ох
СГРУППИРОВАТЬ ПО
ОБ_ох.Контрагенты,
ОБ_ох.StatusОбъекта
Разберем, в чем здесь ошибка. Когда мы добавляем поле СтатусОбъекта в секцию СГРУППИРОВАТЬ ПО, система разделяет данные на уникальные комбинации «Контрагент + Статус». Внутри каждой такой группы значение статуса всегда ровно одно. Следовательно, функция КОЛИЧЕСТВО(РАЗЛИЧНЫЕ СтатусОбъекта) всегда будет возвращать 1 для каждой строки, и мы получим список всех контрагентов со всеми их статусами, что противоречит условию задачи.
Для решения нам нужно сгруппировать данные только по контрагенту, а статус «свернуть» с помощью агрегатной функции, применив условие фильтрации к результату группировки.
Это самый оптимальный и лаконичный способ. Рассмотрим его по шагам. Чтобы получить статус, когда мы точно знаем, что он единственный, мы можем использовать функцию МАКСИМУМ. Поскольку различных значений всего одно, максимум из одного значения будет равен самому этому значению.
Проанализируем правильную структуру запроса:
ВЫБРАТЬ
ОБ_ох.Контрагенты КАК Контрагент,
МАКСИМУМ(ОБ_ох.СтатусОбъекта) КАК СтатусОбъекта
ИЗ
ОБ_ох КАК ОБ_ох
СГРУППИРОВАТЬ ПО
ОБ_ох.Контрагенты
ИМЕЮЩИЕ
КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ОБ_ох.СтатусОбъекта) = 1
Почему это работает?
1. Мы группируем таблицу только по полю Контрагенты. Теперь для каждого контрагента в запросе выделена ровно одна строка.
2. Секция ИМЕЮЩИЕ (аналог HAVING в SQL) накладывает условие на результат агрегации. Она отсеивает тех контрагентов, у которых количество уникальных статусов больше одного (разные статусы у объектов) или равно нулю (нет объектов).
3. В поле выбора мы используем МАКСИМУМ(СтатусОбъекта). Так как условие ИМЕЮЩИЕ гарантирует нам наличие только одного уникального значения, МАКСИМУМ корректно вернет нам именно этот единственный статус.
Если логика отбора контрагентов более сложная, или если вам нужно получить дополнительные данные из других таблиц, разберем подход с использованием временных таблиц. Это сделает код более читаемым и может повысить производительность на больших объемах данных.
Выполним задачу поэтапно:
Шаг 1. Отбор подходящих контрагентов во временную таблицу.
ВЫБРАТЬ
ОБ_ох.Контрагенты КАК Контрагент
ПОМЕСТИТЬ ВТ_ОтобранныеКонтрагенты
ИЗ
ОБ_ох КАК ОБ_ох
СГРУППИРОВАТЬ ПО
ОБ_ох.Контрагенты
ИМЕЮЩИЕ
КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ОБ_ох.СтатусОбъекта) = 1
;
Шаг 2. Соединение временной таблицы с исходными данными для получения статуса.
ВЫБРАТЬ РАЗЛИЧНЫЕ
ВТ.Контрагент,
ОБ_ох.СтатусОбъекта
ИЗ
ВТ_ОтобранныеКонтрагенты КАК ВТ
ВНУТРЕННЕЕ СОЕДИНЕНИЕ ОБ_ох КАК ОБ_ох
ПО ВТ.Контрагент = ОБ_ох.Контрагенты
Этот метод полезен, когда СтатусОбъекта является сложным типом или когда в итоговую выборку нужно добавить множество других полей, которые нельзя просто обернуть в МАКСИМУМ.
Рассмотрим несколько критических моментов, которые помогут избежать ошибок при работе с реальными данными:
КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ...) игнорирует значения NULL. Если у контрагента есть один заполненный статус и три пустых объекта (со статусом NULL), запрос вернет «1», и контрагент попадет в выборку. Если бизнес-логика подразумевает, что NULL — это тоже статус, используйте функцию ЕСТЬNULL(СтатусОбъекта, ЗНАЧЕНИЕ(Перечисление.Статусы.Пусто)) внутри агрегатной функции.КОЛИЧЕСТВО(РАЗЛИЧНЫЕ) довольно ресурсоемка для СУБД (особенно для PostgreSQL). Если таблица ОБ_ох содержит миллионы записей, убедитесь, что поля Контрагент и СтатусОбъекта проиндексированы. В регистрах сведений 1С измерения индексируются автоматически, что делает такие запросы очень быстрыми.ИМЕЮЩИЕ можно вынести в настройки отбора на уровне группировки. Это позволит пользователю самому менять условие (например, выбирать тех, у кого больше двух статусов), не меняя текст запроса.Выясним причину выбора именно агрегатной функции МАКСИМУМ в первом способе. В языке запросов 1С любое поле, не входящее в группировку, обязано быть агрегированным. Поскольку мы отфильтровали данные так, что значение в группе только одно, любая функция — МАКСИМУМ или МИНИМУМ — даст идентичный и правильный результат.
Таким образом, мы проанализировали ситуацию и выяснили, что наиболее эффективным решением является сочетание группировки по основному объекту (Контрагенту) с фильтрацией через секцию ИМЕЮЩИЕ и извлечением значения через МАКСИМУМ. Это обеспечивает высокую скорость выполнения и чистоту кода.