При построении запросов в 1С разработчики часто сталкиваются с ситуацией, когда итоговая таблица содержит дублирующиеся строки (результат «задваивается» или «затраивается»). Особенно часто это происходит при левом соединении источника данных с виртуальной таблицей СрезПоследних регистра сведений.
В этой статье мы подробно разберем причину такого поведения запросов, проанализируем ошибки при группировке и рассмотрим правильные варианты решения этой задачи.
Рассмотрим механику работы виртуальной таблицы СрезПоследних. Она возвращает срез наиболее актуальных записей на указанную дату в разрезе всех измерений регистра сведений.
Если в регистре сведений (например, КВП_ПлощадьПомещения) помимо базового измерения Объект (помещение) присутствует второе измерение — ВидПлощади (общая, жилая, площадь земельного участка и т.д.), то для одного помещения в регистре хранится несколько актуальных записей — по одной на каждый вид площади.
Когда мы выполняем следующее ЛЕВОЕ СОЕДИНЕНИЕ:
|ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КВП_ПлощадьПомещения.СрезПоследних(&Дата, ) КАК КВП_ПлощадьПомещенияСрезПоследних
|ПО СписокЛС.Помещение = КВП_ПлощадьПомещенияСрезПоследних.Объект
СУБД соединяет одну строку лицевого счета со всеми найденными записями среза последней по данному помещению. В результате одна строка лицевого счета размножается на столько строк, сколько видов площадей зафиксировано для этого объекта в регистре.
Попытка просто добавить конструкцию СГРУППИРОВАТЬ ПО со всеми полями выборки или выражением ВЫБОР КОГДА ... не убирает дубли. Посмотрим на причину: если условная конструкция в секции ВЫБОР перечислена в СГРУППИРОВАТЬ ПО без использования агрегатных функций (МАКСИМУМ, СУММА), каждая комбинация значений образует отдельную группу. Записи всё равно останутся уникальными в разрезе видов площадей и не свернутся.
Наиболее наглядный способ получить различные виды показателей (в данном случае — виды площадей) в отдельные колонки одной строки — выполнить индивидуальное соединение с виртуальной таблицей СрезПоследних для каждого вида площади. Ключевой момент — передача условия отбора непосредственно в параметры виртуальной таблицы.
Разберем готовый пример такого запроса:
ВЫБРАТЬ
СписокЛС.ЛицевойСчет КАК ЛицевойСчет,
СписокЛС.Собственник КАК Собственник,
СписокЛС.Помещение КАК Помещение,
СписокЛС.Этаж КАК Этаж,
СписокЛС.КоличествоКомнат КАК КоличествоКомнат,
СписокЛС.Здание КАК Здание,
СписокЛС.Здание.Многоквартирное КАК ПризнакМКД,
КВП_АдресаСрезПоследних.Город КАК Город,
КВП_АдресаСрезПоследних.НаселенныйПункт КАК НаселенныйПункт,
КВП_АдресаСрезПоследних.Улица КАК Улица,
КВП_АдресаСрезПоследних.Дом КАК Дом,
КВП_АдресаСрезПоследних.Корпус КАК Корпус,
ЕСТЬNULL(ОбщПл.Площадь, 0) КАК ОбщаяПлощадь,
ЕСТЬNULL(ЖилПл.Площадь, 0) КАК ЖилаяПлощадь,
ЕСТЬNULL(ЗемУчПл.Площадь, 0) КАК ПлощадьЗемельногоУчастка
ИЗ
СписокЛС КАК СписокЛС
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КВП_Адреса.СрезПоследних(&Дата, ) КАК КВП_АдресаСрезПоследних
ПО СписокЛС.Здание = КВП_АдресаСрезПоследних.Объект
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КВП_ПлощадьПомещения.СрезПоследних(&Дата, ВидПлощади.Код = "000000001") КАК ОбщПл
ПО СписокЛС.Помещение = ОбщПл.Объект
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КВП_ПлощадьПомещения.СрезПоследних(&Дата, ВидПлощади.Код = "000000002") КАК ЖилПл
ПО СписокЛС.Помещение = ЖилПл.Объект
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КВП_ПлощадьПомещения.СрезПоследних(&Дата, ВидПлощади.Код = "000000007") КАК ЗемУчПл
ПО СписокЛС.Помещение = ЗемУчПл.Объект
Обратите внимание: Для предотвращения появления значений NULL при отсутствии записи по какому-либо виду площади рекомендуется оборачивать поля в функцию ЕСТЬNULL(..., 0).
Если необходимо соединяться с виртуальной таблицей регистра сведений только один раз, можно развернуть строки в колонки с помощью группировки и агрегатных функций. В этом случае мы делаем одно левое соединение со срезом последних по всем нужным видам площадей, а затем сворачиваем результат:
ВЫБРАТЬ
СписокЛС.ЛицевойСчет КАК ЛицевойСчет,
СписокЛС.Помещение КАК Помещение,
МАКСИМУМ(ВЫБОР
КОГДА КВП_ПлощадьПомещенияСрезПоследних.ВидПлощади.Код = "000000001"
ТОГДА КВП_ПлощадьПомещенияСрезПоследних.Площадь
ИНАЧЕ 0
КОНЕЦ) КАК ОбщаяПлощадь,
МАКСИМУМ(ВЫБОР
КОГДА КВП_ПлощадьПомещенияСрезПоследних.ВидПлощади.Код = "000000002"
ТОГДА КВП_ПлощадьПомещенияСрезПоследних.Площадь
ИНАЧЕ 0
КОНЕЦ) КАК ЖилаяПлощадь
ИЗ
СписокЛС КАК СписокЛС
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КВП_ПлощадьПомещения.СрезПоследних(&Дата, ) КАК КВП_ПлощадьПомещенияСрезПоследних
ПО СписокЛС.Помещение = КВП_ПлощадьПомещенияСрезПоследних.Объект
СГРУППИРОВАТЬ ПО
СписокЛС.ЛицевойСчет,
СписокЛС.Помещение
При таком подходе агрегатная функция МАКСИМУМ (или СУММА) оставляет нужное числовое значение для соответствующего вида площади и схлопывает нулевые значения остальных строк группы.
Для оптимизации скорости выполнения запросов на больших объемах данных рекомендуется предварительно подготавливать данные во временных таблицах:
ВТ_Помещения и проиндексировать поле соединения.ВТ_Помещения, наложив условие в параметре виртуальной таблицы: Объект В (ВЫБРАТЬ Помещение ИЗ ВТ_Помещения).Это исключит лишние соединения с неиспользуемыми записями регистра и существенно повысит производительность запроса на СУБД.