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