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