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