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