Почему запрос в 1С с группировкой и функцией КОЛИЧЕСТВО работает некорректно и выводит дубли колонок вместо чисел

Программист 1С v8.3 (Управляемые формы) Сфера услуг, туризм и социальный сектор
← На главную

При написании запросов в платформе 1С:Предприятие разработчики периодически сталкиваются с ситуацией, когда логически верный запрос возвращает неожиданный результат: вместо числовых значений агрегатов появляются дублирующиеся представления ссылочных полей или неверные подсчеты строк. Разберем подробнее причины такого поведения платформы и проанализируем, как правильно составлять подобные запросы.

Анализ проблемы: почему стандартная группировка дает сбой

Рассмотрим ситуацию на конкретном примере. Требуется выбрать здания (владельцев лицевых счетов), у которых подключены определенные услуги (например, «Отопление» или «ГВС»), и посчитать количество таких услуг по каждому зданию.

При формировании запроса часто допускаются следующие типичные ошибки:

  1. Использование агрегатной функции по полю группировки. Если в секции СГРУППИРОВАТЬ ПО используется ссылочное поле (например, ЛицевойСчетВладелец), и от этого же поля берется функция КОЛИЧЕСТВО(ЛицевойСчетВладелец), в платформе срабатывает механизм автопредставления. Вместо подсчета количества записей транслятор запросов пытается сформировать представление объекта. В результате в выборку выдаются две одинаковые колонки с текстовым представлением ссылки без числовых итогов.
  2. Неявные левые соединения (разыменование полей через точку). При обращении к реквизитам через точку (например, ПодключенныеУслугиСрезПоследних.ЛицевойСчет.ЗакрытыйЛС) СУБД генерирует неявный LEFT JOIN. При выполнении условия с логическим отрицанием (НЕ ...ЗакрытыйЛС) на поля со значением NULL результат дает NULL, а не ИСТИНА, из-за чего часть строк отсекается.
  3. Использование условий в параметрах виртуальной таблицы среза последних. Условие вида (Услуга = &Отопление ИЛИ Услуга = &ГВС) в параметрах виртуальной таблицы отбирает записи до формирования среза. Если измерением регистра является только лицевой счет, будет получена только последняя запись, что исказит итоговое количество услуг.

Способ 1. Исправление подсчета во временной таблице

Если для бизнес-логики необходимо использовать временные таблицы, выясним, как правильно выполнять подсчет. Во временной таблице ЗД собираются уникальные пары «Здание – Услуга». При последующей группировке нельзя использовать функцию КОЛИЧЕСТВО от поля группировки ЛицевойСчетВладелец.

Подсчет следует выполнять либо по полю, уникальному для каждой строки внутри группы (например, Услуга), либо с использованием константы КОЛИЧЕСТВО(1):


ВЫБРАТЬ РАЗЛИЧНЫЕ
	ПодключенныеУслугиСрезПоследних.ЛицевойСчет.Владелец КАК ЛицевойСчетВладелец,
	ПодключенныеУслугиСрезПоследних.Услуга КАК Услуга
ПОМЕСТИТЬ ЗД
ИЗ
	РегистрСведений.ПодключенныеУслуги.СрезПоследних(, Услуга = &Отопление ИЛИ Услуга = &ГВС) КАК ПодключенныеУслугиСрезПоследних
ГДЕ
	ПодключенныеУслугиСрезПоследних.Вкл
	И НЕ ПодключенныеУслугиСрезПоследних.ЛицевойСчет.ЗакрытыйЛС
	И НЕ ПодключенныеУслугиСрезПоследних.ЛицевойСчет.ЭтоОрганизация
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
	ЗД.ЛицевойСчетВладелец КАК ЛицевойСчетВладелец,
	КОЛИЧЕСТВО(ЗД.Услуга) КАК КолвоУслуг
ИЗ
	ЗД КАК ЗД
СГРУППИРОВАТЬ ПО
	ЗД.ЛицевойСчетВладелец

Способ 2. Оптимальное решение в один запрос без временных таблиц

Создание промежуточных временных таблиц для простой группировки создает избыточную нагрузку на СУБД. Проанализируем, как получить тот же результат в один запрос с помощью секции СГРУППИРОВАТЬ ПО и функций агрегации по конкретному полю ссылки:


ВЫБРАТЬ
	ПодключенныеУслугиСрезПоследних.ЛицевойСчет.Владелец КАК Владелец,
	КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ПодключенныеУслугиСрезПоследних.Услуга) КАК КолвоУслуг
ИЗ
	РегистрСведений.ПодключенныеУслуги.СрезПоследних(, Услуга = &Отопление ИЛИ Услуга = &ГВС) КАК ПодключенныеУслугиСрезПоследних
ГДЕ
	ПодключенныеУслугиСрезПоследних.Вкл
	И НЕ ПодключенныеУслугиСрезПоследних.ЛицевойСчет.ЗакрытыйЛС
	И НЕ ПодключенныеУслугиСрезПоследних.ЛицевойСчет.ЭтоОрганизация
СГРУППИРОВАТЬ ПО
	ПодключенныеУслугиСрезПоследних.ЛицевойСчет.Владелец

Рекомендации по повышению производительности

Чтобы избежать неявных соединений и падения производительности на больших объемах данных, придерживаемся следующих правил:

  1. Заменяем разыменование полей через точку (ЛицевойСчет.ЗакрытыйЛС) на явное ВНУТРЕННЕЕ СОЕДИНЕНИЕ (INNER JOIN) со справочником ЛицевыеСчетаАбонентов.
  2. Условия отбора по реквизитам лицевого счета переносим в условие соединения ПО.
  3. Если требуется отфильтровать итоговый результат по количеству услуг, используем секцию ИМЕЮЩИЕ вместо вложенного запроса с секцией ГДЕ.
← На главную