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