Собственно, всё упихнуто в одну функцию, и стандартная свёртка тоже. Проверено на практике, но на тестовом массиве с небольшим разнообразием. Я вот пока не очень уверен, что в любом случае будет работать — например, при каких-нибудь экзотических типах колонок или их наполнении. Но если кому пригодится, и хорошо. Как пример работы с СКД, кстати )))
// рИсточник — собственно объект-источник (обычно таблица значений, её имя определяется автоматически)
Функция ПоместитьРезультатСКДвТаблицуЗначений(рСКД,рКомпНастроекИлиНастройки,рИсточник=«») Экспорт
Попытка
РезультатнаяТаблица=Новый ТаблицаЗначений;
//
// исходя из типа источника
рНабор=рСКД.НаборыДанных.Получить(0); // основной набор
//
КомпоновщикМакета=Новый КомпоновщикМакетаКомпоновкиДанных;
рТипГенератора=Тип(«ГенераторМакетаКомпоновкиДанныхДляКоллекцииЗначений»);
Если ТипЗнч(рКомпНастроекИлиНастройки)=Тип(«НастройкиКомпоновкиДанных») Тогда
МакетКомпоновки=КомпоновщикМакета.Выполнить(рСКД,рКомпНастроекИлиНастройки,,,рТипГенератора);
ИначеЕсли ТипЗнч(рКомпНастроекИлиНастройки)=Тип(«КомпоновщикНастроекКомпоновкиДанных») Тогда
МакетКомпоновки=КомпоновщикМакета.Выполнить(рСКД,рКомпНастроекИлиНастройки.Настройки,,,рТипГенератора);
КонецЕсли;
ПроцессорКД=Новый ПроцессорКомпоновкиДанных;
Если ТипЗнч(рНабор)=Тип(«НаборДанныхОбъектСхемыКомпоновкиДанных») Тогда
ПроцессорКД.Инициализировать(МакетКомпоновки,Новый Структура(СокрЛП(рНабор.ИмяОбъекта),рИсточник));
Иначе
ПроцессорКД.Инициализировать(МакетКомпоновки);
КонецЕсли;
ПроцессорВывода=Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВКоллекциюЗначений;
ПроцессорВывода.УстановитьОбъект(РезультатнаяТаблица);
#Если Клиент Тогда
ПроцессорВывода.ОтображатьПроцентВывода=Истина;
#КонецЕсли
РезультатнаяТаблица=ПроцессорВывода.Вывести(ПроцессорКД,Истина);
Возврат РезультатнаяТаблица;
Исключение
Сообщить(«ПоместитьРезультатСКДвТаблицуЗначений: ошибка: «+ОписаниеОшибки(),СтатусСообщения.ОченьВажное);
Возврат РезультатнаяТаблица;
КонецПопытки;
КонецФункции
// Свёртка таблицы, возвращаемое значение типа «ТаблицаЗначений»
// Параметры:
// рТаблица — исходная таблица значений; допустимы любые типы колонок, доступные к выборке в СКД;
// рПоляСвертки — имена колонок таблицы через запятую; по их одинаковым значениям будет выполняться группировка;
// если строка пуста, и не пуста строка рПоляСуммы, то все колонки, не входящие в суммируемые, войдут в группируемые.
// рПоляСумма — имена колонок таблицы через запятую, значения этих колонок суммируются по общим принципам свёртки
// и суммирования полей в СКД; если строка пуста, то: а) при пустой строке свёртки будет предпринята попытка заполнить
// строку именами колонок, имеющих числовой тип (хотя бы и среди других типов), и если не удастся, завершит функцию;
// б) при непустой строке свёртки и непустом имени колонки-агрегатора выполнит свёртку, не суммируя ни по какому полю;
// в) при непустой строке свёртки и пустом имени колонки-агрегатора выполнит обычную свёртку без суммовых колонок;
// рИмяКолонкиАгрегатора — необязательное имя поля колонки для вложенных таблиц значений; если не указано, происходит
// обычная свёртка средствами 1С, а если указано, в результатную таблицу вносится колонка с этим именем, содержащая
// таблицы значений «фрагментов» (блоков), соответствующих правилам свёртки; причём колонки, участвующие в суммиро-
// вании (т.е. из рПоляСуммы) также в исходном, непросуммированном виде присутствуют в этих вложеных подтаблицах.
//
Функция СвернутьТаблицу(Знач рТаблица,Знач рПоляСвертки,Знач рПоляСуммы,рИмяКолонкиАгрегатора=«») Экспорт
Попытка
мПоляСвертки=ОбщегоНазначения.РазложитьСтрокуВМассивПодстрок(рПоляСвертки,«,»);
мПоляСуммы=ОбщегоНазначения.РазложитьСтрокуВМассивПодстрок(рПоляСуммы,«,»);
Если ТипЗнч(рТаблица)<>Тип(«ТаблицаЗначений») Тогда Возврат рТаблица КонецЕсли;
Если рТаблица.Колонки.Количество()=0 Тогда Возврат рТаблица КонецЕсли;
Если мПоляСвертки.Количество()=0 и мПоляСуммы.Количество()=0 Тогда
// определяем по типам значений
рПоляСуммы=«»; разд=«»;
Для каждого кол Из рТаблица.Колонки Цикл
Если кол.ТипЗначения.СодержитТип(Тип(«Число»)) Тогда
мПоляСуммы.Добавить(СокрЛП(кол.Имя));
рПоляСуммы=рПоляСуммы+разд+СокрЛП(кол.Имя); разд=«,»;
КонецЕсли;
КонецЦикла;
Если мПоляСуммы.Количество()=0 Тогда // ничего поделать нельзя
Возврат рТаблица;
КонецЕсли;
КонецЕсли;
Если мПоляСвертки.Количество()=0 и мПоляСуммы.Количество()<>0 Тогда
// все поля, не входящие в суммируемые, подразумеваются как свёрточные
рПоляСвертки=«»; разд=«»;
Для каждого кол Из рТаблица.Колонки Цикл
Если мПоляСуммы.Найти(СокрЛП(кол.Имя))=Неопределено Тогда
мПоляСвертки.Добавить(СокрЛП(кол.Имя));
рПоляСвертки=рПоляСвертки+разд+СокрЛП(кол.Имя); разд=«,»;
КонецЕсли;
КонецЦикла;
КонецЕсли;
Если ПустаяСтрока(рИмяКолонкиАгрегатора) Тогда // режим агрегирования не используется
рТаблица.Свернуть(рПоляСвертки,рПоляСуммы);
КонецЕсли;
Если ПустаяСтрока(рПоляСвертки)
или мПоляСвертки.Количество()=0
или ПустаяСтрока(рИмяКолонкиАгрегатора)
Тогда Возврат рТаблица КонецЕсли;
рСКД=Новый СхемаКомпоновкиДанных;
//
рИсточникДанных=рСКД.ИсточникиДанных.Добавить();
рИсточникДанных.Имя=«ОсновнойИсточник»;
рИсточникДанных.ТипИсточникаДанных=«Local»;
//
рНабор=рСКД.НаборыДанных.Добавить(Тип(«НаборДанныхОбъектСхемыКомпоновкиДанных»));
рНабор.Имя=«ОсновнойНабор»;
рНабор.ИмяОбъекта=«ТаблицаИсточник»; // связывание с внешними данными идёт именно по нему
рНабор.ИсточникДанных=«ОсновнойИсточник»;
//
рНастройка=рСКД.НастройкиПоУмолчанию;
рГруппировкаКД=рНастройка.Структура.Добавить(Тип(«ГруппировкаКомпоновкиДанных»));
Для каждого рИмяПоляСвертки Из мПоляСвертки Цикл
рПолеГр=рГруппировкаКД.ПоляГруппировки.Элементы.Добавить(Тип(«ПолеГруппировкиКомпоновкиДанных»));
рПолеГр.Поле=Новый ПолеКомпоновкиДанных(СокрЛП(рИмяПоляСвертки));
рПолеГр.ТипГруппировки=ТипГруппировкиКомпоновкиДанных.Элементы;
КонецЦикла;
рГруппировкаКД.Выбор.Элементы.Добавить(Тип(«АвтоВыбранноеПолеКомпоновкиДанных»));
//
мОстающихся=Новый Массив;
стрОстающихся=«»; разд=«»;
Для каждого кол Из рТаблица.Колонки Цикл
#Если Клиент Тогда
ОбработкаПрерыванияПользователя();
#КонецЕсли
// вносим поля в набор, это обязательно
рПоле=рНабор.Поля.Добавить(Тип(«ПолеНабораДанныхСхемыКомпоновкиДанных»));
рПоле.Заголовок=кол.Заголовок;
рПоле.Поле=кол.Имя;
рПоле.ПутьКДанным=кол.Имя;
рПоле.ТипЗначения=СКДиПостроители.ПолучитьОписаниеТипаБезПустых(кол.ТипЗначения);
// выбранные поля добавляем на уровень самой настройки, т.е. группы «Отчёт»
рВыбПоле=рНастройка.Выбор.Элементы.Добавить(Тип(«ВыбранноеПолеКомпоновкиДанных»));
рВыбПоле.Заголовок=рПоле.Заголовок;
рВыбПоле.Поле=Новый ПолеКомпоновкиДанных(рПоле.ПутьКДанным);
// выясним, куда попадает эта колонка
Если мПоляСвертки.Найти(СокрЛП(кол.Имя))=Неопределено Тогда // она может быть и сумматорной
мОстающихся.Добавить(кол.Имя);
стрОстающихся=стрОстающихся+разд+кол.Имя; разд=«,»;
КонецЕсли;
КонецЦикла;
Если ПустаяСтрока(стрОстающихся) Тогда // вообще ничего, по сути, не надо
рТаблица.Свернуть(СокрЛП(рПоляСвертки));
Возврат рТаблица;
КонецЕсли;
// вносим само поле-вычислитель
рВыражение=«ВЫБОР КОГДА Уровень()=0 ТОГДА ВычислитьВыражение(«»ТаблицаЗначений(«+стрОстающихся+«)»»,,,»»Текущая»»,»»Последняя»») КОНЕЦ»;
рВычПоле=рСКД.ВычисляемыеПоля.Добавить();
рВычПоле.ПутьКДанным=СокрЛП(рИмяКолонкиАгрегатора);
рВычПоле.Заголовок=«Таблицы»;
рВычПоле.Выражение=рВыражение;
//
рИтПоле=рСКД.ПоляИтога.Добавить();
рИтПоле.Выражение=СокрЛП(рИмяКолонкиАгрегатора);
Для каждого рИмяПоляСвертки Из мПоляСвертки Цикл
рИтПоле.Группировки.Добавить(СокрЛП(рИмяПоляСвертки));
КонецЦикла;
рИтПоле.ПутьКДанным=СокрЛП(рИмяКолонкиАгрегатора);
//
Для каждого рИмяПоляСуммы Из мПоляСуммы Цикл
рИтПоле=рСКД.ПоляИтога.Добавить();
рИтПоле.Выражение=«Сумма(«+СокрЛП(рИмяПоляСуммы)+«)»;
рИтПоле.ПутьКДанным=СокрЛП(рИмяПоляСуммы);
// а группировки оставим пустыми, чтобы шёл по всем
КонецЦикла;
//
рВыбПоле=рНастройка.Выбор.Элементы.Добавить(Тип(«ВыбранноеПолеКомпоновкиДанных»));
рВыбПоле.Заголовок=«Таблицы»;
рВыбПоле.Поле=Новый ПолеКомпоновкиДанных(рВычПоле.ПутьКДанным);
рКомпоновщикН=Новый КомпоновщикНастроекКомпоновкиДанных;
рКомпоновщикН.Инициализировать(Новый ИсточникДоступныхНастроекКомпоновкиДанных(рСКД));
рКомпоновщикН.ЗагрузитьНастройки(рНастройка);
резТаблица=СКДиПостроители.ПоместитьРезультатСКДвТаблицуЗначений(рсКД,рКомпоновщикН,рТаблица);
Если резТаблица.Количество()<>0 Тогда
послстро=резТаблица[резТаблица.Количество()-1];
Если ТипЗнч(послстро[СокрЛП(рИмяКолонкиАгрегатора)])<>Тип(«ТаблицаЗначений») Тогда
резТаблица.Удалить(послстро);
КонецЕсли;
КонецЕсли;
//
Возврат резТаблица;
Исключение
Сообщить(«СвернутьТаблицу, ошибка: «+ОписаниеОшибки(),СтатусСообщения.ОченьВажное);
Возврат рТаблица;
КонецПопытки;
КонецФункции
Внимание! Если у кого-то не заработает или будет работать неверно — пожалуйста, сообщите, постараюсь оперативно разобраться.
Пы.Сы. Не уверен, что за такую фиговинку имеет смысл плюсить.
А зачем? Всегда можно оперировать копией ТЗ.
Можно. Только копия ресурс кушает, если большая, и «оперировать», т.е. искать даже по индексированной — иногда скучное дело. Я всего лишь показал альтернативу, когда «всё в одном». ))
жениться тебе надо
тогда будет жаль времени на всякую ненужную ботву
Как пример простой программной работы с СКД очень хорошо. Стоило преподносить публикацию именно как пример работы с СКД, тогда польза от нее очевидна.
Смысл тогда сворачивать? Если нужны списки, так и надо в 3-й колонке значением список делать
(3) Позвольте представить, моя жена:http://infostart.ru/community/profile/41675/ — и уже восемь лет вместе )))
(5) Смысл простой — и свернуть надо, и данные не потерять. А список или массив хороши, когда такая колонка одна. Если было 10 колонок, по 1-й и 3-й свернули, пятую суммировали, то, когда нужны все остальные, подтаблица удобнее списка.
честно говоря, пока для себя не вижу практического применения данного решения
если я что-то сворачиваю, я это сворачиваю. Если не надо сворачивать, я не сворачиваю.
Но всё же плюс, за конструктивные ответы (и красивую жену 🙂 )
В семерки часто сталкиваюсь с такой задачей. Что нужно данные сохранять, после свертки. В частности при формировании группировок в отчетах. Но, ИМХО, в такой ситуации, лучше, действительно, оперировать таблицей значений. А уж если ресурсы жалко (когда, например, нужно работать с 100500 записями). Лучше использовать временные таблицы.
(7) Мне часто приходится сталкиваться, раньше делал через 2 таблицы (свёрнутая и исходная), но оперативку жалко. А так пыхтит сервер приложения, насколько я понял распределение нагрузки. И что-то мне подсказывает, что напрячь механизм СКД в этом случае более экономно, нежели крутить временные таблицы.
А если честно, просто захотелось побаловаться с этими функциями СКД, и всё ))
не смотря на комментарии, для меня всё равно полезно было ознакомиться, спасибо
(10) TrinitronOTV, Кто к нам пожаловал…
(0) СКДиПостроители — это откуда? не встречал такого модуля.
(0) Присоединяюсь к (12)
Если это твой модуль, было бы логично выложить и его (в части, касающейся данного решения).
Ёлы-палы, сколько ж я ещё на эти грабли наступать буду… Ща, выложу нужные функции.
ок, ПоместитьРезультатСКДвТаблицуЗначений() уже есть, но нужна еще ПолучитьОписаниеТипаБезПустых()
(15) Она совсем простая и необязательная:
Функция ПолучитьОписаниеТипаБезПустых(рОписТипов) Экспорт
мТипов=Новый Массив;
Для каждого рТип Из рОписТипов.Типы() Цикл
Если рТип=Тип(«Неопределено») или рТип=Тип(«NULL») или рТип=Неопределено или рТип=Null Тогда Продолжить КонецЕсли;
мТипов.Добавить(рТип);
КонецЦикла;
Возврат Новый ОписаниеТипов(мТипов,рОписТипов.КвалификаторыЧисла,рОписТипов.КвалификаторыСтроки,рОписТипов.КвалификаторыДаты);
КонецФункции
(0)
Вот именно за это плюс и поставлю 😉
не поняда. Зачем?