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