Как избавиться от задваивания результатов в запросе 1С при соединении с виртуальной таблицей «СрезПоследних»?

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

При построении запросов в 1С разработчики часто сталкиваются с ситуацией, когда итоговая таблица содержит дублирующиеся строки (результат «задваивается» или «затраивается»). Особенно часто это происходит при левом соединении источника данных с виртуальной таблицей СрезПоследних регистра сведений.

В этой статье мы подробно разберем причину такого поведения запросов, проанализируем ошибки при группировке и рассмотрим правильные варианты решения этой задачи.

Почему происходит задваивание данных?

Рассмотрим механику работы виртуальной таблицы СрезПоследних. Она возвращает срез наиболее актуальных записей на указанную дату в разрезе всех измерений регистра сведений.

Если в регистре сведений (например, КВП_ПлощадьПомещения) помимо базового измерения Объект (помещение) присутствует второе измерение — ВидПлощади (общая, жилая, площадь земельного участка и т.д.), то для одного помещения в регистре хранится несколько актуальных записей — по одной на каждый вид площади.

Когда мы выполняем следующее ЛЕВОЕ СОЕДИНЕНИЕ:


|ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КВП_ПлощадьПомещения.СрезПоследних(&Дата, ) КАК КВП_ПлощадьПомещенияСрезПоследних
|ПО СписокЛС.Помещение = КВП_ПлощадьПомещенияСрезПоследних.Объект

СУБД соединяет одну строку лицевого счета со всеми найденными записями среза последней по данному помещению. В результате одна строка лицевого счета размножается на столько строк, сколько видов площадей зафиксировано для этого объекта в регистре.

Почему обычная группировка `СГРУППИРОВАТЬ ПО` не помогает?

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

Вариант решения 1: Использование раздельных левых соединений с фильтром в параметрах ВТ

Наиболее наглядный способ получить различные виды показателей (в данном случае — виды площадей) в отдельные колонки одной строки — выполнить индивидуальное соединение с виртуальной таблицей СрезПоследних для каждого вида площади. Ключевой момент — передача условия отбора непосредственно в параметры виртуальной таблицы.

Разберем готовый пример такого запроса:


ВЫБРАТЬ
    СписокЛС.ЛицевойСчет КАК ЛицевойСчет,
    СписокЛС.Собственник КАК Собственник,
    СписокЛС.Помещение КАК Помещение,
    СписокЛС.Этаж КАК Этаж,
    СписокЛС.КоличествоКомнат КАК КоличествоКомнат,
    СписокЛС.Здание КАК Здание,
    СписокЛС.Здание.Многоквартирное КАК ПризнакМКД,
    КВП_АдресаСрезПоследних.Город КАК Город,
    КВП_АдресаСрезПоследних.НаселенныйПункт КАК НаселенныйПункт,
    КВП_АдресаСрезПоследних.Улица КАК Улица,
    КВП_АдресаСрезПоследних.Дом КАК Дом,
    КВП_АдресаСрезПоследних.Корпус КАК Корпус,
    ЕСТЬNULL(ОбщПл.Площадь, 0) КАК ОбщаяПлощадь,
    ЕСТЬNULL(ЖилПл.Площадь, 0) КАК ЖилаяПлощадь,
    ЕСТЬNULL(ЗемУчПл.Площадь, 0) КАК ПлощадьЗемельногоУчастка
ИЗ
    СписокЛС КАК СписокЛС
    ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КВП_Адреса.СрезПоследних(&Дата, ) КАК КВП_АдресаСрезПоследних
        ПО СписокЛС.Здание = КВП_АдресаСрезПоследних.Объект

    ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КВП_ПлощадьПомещения.СрезПоследних(&Дата, ВидПлощади.Код = "000000001") КАК ОбщПл
        ПО СписокЛС.Помещение = ОбщПл.Объект

    ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КВП_ПлощадьПомещения.СрезПоследних(&Дата, ВидПлощади.Код = "000000002") КАК ЖилПл
        ПО СписокЛС.Помещение = ЖилПл.Объект

    ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КВП_ПлощадьПомещения.СрезПоследних(&Дата, ВидПлощади.Код = "000000007") КАК ЗемУчПл
        ПО СписокЛС.Помещение = ЗемУчПл.Объект

Обратите внимание: Для предотвращения появления значений NULL при отсутствии записи по какому-либо виду площади рекомендуется оборачивать поля в функцию ЕСТЬNULL(..., 0).

Вариант решения 2: Использование агрегатных функций (СУММА / МАКСИМУМ)

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


ВЫБРАТЬ
    СписокЛС.ЛицевойСчет КАК ЛицевойСчет,
    СписокЛС.Помещение КАК Помещение,
    МАКСИМУМ(ВЫБОР
        КОГДА КВП_ПлощадьПомещенияСрезПоследних.ВидПлощади.Код = "000000001"
            ТОГДА КВП_ПлощадьПомещенияСрезПоследних.Площадь
        ИНАЧЕ 0
    КОНЕЦ) КАК ОбщаяПлощадь,
    МАКСИМУМ(ВЫБОР
        КОГДА КВП_ПлощадьПомещенияСрезПоследних.ВидПлощади.Код = "000000002"
            ТОГДА КВП_ПлощадьПомещенияСрезПоследних.Площадь
        ИНАЧЕ 0
    КОНЕЦ) КАК ЖилаяПлощадь
ИЗ
    СписокЛС КАК СписокЛС
    ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КВП_ПлощадьПомещения.СрезПоследних(&Дата, ) КАК КВП_ПлощадьПомещенияСрезПоследних
        ПО СписокЛС.Помещение = КВП_ПлощадьПомещенияСрезПоследних.Объект
СГРУППИРОВАТЬ ПО
    СписокЛС.ЛицевойСчет,
    СписокЛС.Помещение

При таком подходе агрегатная функция МАКСИМУМ (или СУММА) оставляет нужное числовое значение для соответствующего вида площади и схлопывает нулевые значения остальных строк группы.

Вариант решения 3: Предварительный выбор во временную таблицу

Для оптимизации скорости выполнения запросов на больших объемах данных рекомендуется предварительно подготавливать данные во временных таблицах:

  1. Поместить исходный список объектов во временную таблицу ВТ_Помещения и проиндексировать поле соединения.
  2. Выбрать срез последних из регистра сведений только для помещений из ВТ_Помещения, наложив условие в параметре виртуальной таблицы: Объект В (ВЫБРАТЬ Помещение ИЗ ВТ_Помещения).
  3. Свернуть полученные площади во временную таблицу перед финальным соединением.

Это исключит лишние соединения с неиспользуемыми записями регистра и существенно повысит производительность запроса на СУБД.

← На главную