Свёртка таблицы без потери данных

05.07.13

Разработка - Универсальные функции

Было в таблице значений 3 колонки, по одной группировали, по другой суммировали, а третья и потерялась... Знакомая сказка? Чтобы не плодить вспомогательные данные, эта функция сворачивает таблицу так, что в специальной колонке будут подтаблицы, хранящие "свёрнутые" куски в первозданном виде. Может быть интересно изучающим некоторые фичи СКД.

Собственно, всё упихнуто в одну функцию, и стандартная свёртка тоже. Проверено на практике, но на тестовом массиве с небольшим разнообразием. Я вот пока не очень уверен, что в любом случае будет работать - например, при каких-нибудь экзотических типах колонок или их наполнении. Но если кому пригодится, и хорошо. Как пример работы с СКД, кстати ))) 

// рИсточник - собственно объект-источник (обычно таблица значений, её имя определяется автоматически)
Функция ПоместитьРезультатСКДвТаблицуЗначений(рСКД,рКомпНастроекИлиНастройки,рИсточник="") Экспорт
Попытка
   
РезультатнаяТаблица=Новый ТаблицаЗначений;
   
//
    // исходя из типа источника
   
рНабор=рСКД.НаборыДанных.Получить(0); // основной набор
    //
   
КомпоновщикМакета=Новый КомпоновщикМакетаКомпоновкиДанных;
   
рТипГенератора=Тип("ГенераторМакетаКомпоновкиДанныхДляКоллекцииЗначений");
    Если
ТипЗнч(рКомпНастроекИлиНастройки)=Тип("НастройкиКомпоновкиДанных") Тогда
       
МакетКомпоновки=КомпоновщикМакета.Выполнить(рСКД,рКомпНастроекИлиНастройки,,,рТипГенератора);
    ИначеЕсли
ТипЗнч(рКомпНастроекИлиНастройки)=Тип("КомпоновщикНастроекКомпоновкиДанных") Тогда
       
МакетКомпоновки=КомпоновщикМакета.Выполнить(рСКД,рКомпНастроекИлиНастройки.Настройки,,,рТипГенератора);
    КонецЕсли;
   
ПроцессорКД=Новый ПроцессорКомпоновкиДанных;
    Если
ТипЗнч(рНабор)=Тип("НаборДанныхОбъектСхемыКомпоновкиДанных") Тогда
       
ПроцессорКД.Инициализировать(МакетКомпоновки,Новый Структура(СокрЛП(рНабор.ИмяОбъекта),рИсточник));
    Иначе
       
ПроцессорКД.Инициализировать(МакетКомпоновки);
    КонецЕсли;
   
ПроцессорВывода=Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВКоллекциюЗначений;
   
ПроцессорВывода.УстановитьОбъект(РезультатнаяТаблица);
   
#Если Клиент Тогда
       
ПроцессорВывода.ОтображатьПроцентВывода=Истина;
   
#КонецЕсли
   
РезультатнаяТаблица=ПроцессорВывода.Вывести(ПроцессорКД,Истина);
    Возврат
РезультатнаяТаблица;
Исключение
   
Сообщить("ПоместитьРезультатСКДвТаблицуЗначений: ошибка: "+ОписаниеОшибки(),СтатусСообщения.ОченьВажное);
    Возврат
РезультатнаяТаблица;
КонецПопытки;
КонецФункции

// Свёртка таблицы, возвращаемое значение типа "ТаблицаЗначений"
// Параметры:
//      рТаблица - исходная таблица значений; допустимы любые типы колонок, доступные к выборке в СКД;
//      рПоляСвертки - имена колонок таблицы через запятую; по их одинаковым значениям будет выполняться группировка;
//           если строка пуста, и не пуста строка рПоляСуммы, то все колонки, не входящие в суммируемые, войдут в группируемые.
//      рПоляСумма - имена колонок таблицы через запятую, значения этих колонок суммируются по общим принципам свёртки
//           и суммирования полей в СКД; если строка пуста, то: а) при пустой строке свёртки будет предпринята попытка заполнить
//           строку именами колонок, имеющих числовой тип (хотя бы и среди других типов), и если не удастся, завершит функцию;
//           б) при непустой строке свёртки и непустом имени колонки-агрегатора выполнит свёртку, не суммируя ни по какому полю;
//           в) при непустой строке свёртки и пустом имени колонки-агрегатора выполнит обычную свёртку без суммовых колонок;
//      рИмяКолонкиАгрегатора - необязательное имя поля колонки для вложенных таблиц значений; если не указано, происходит
//           обычная свёртка средствами 1С, а если указано, в результатную таблицу вносится колонка с этим именем, содержащая
//           таблицы значений "фрагментов" (блоков), соответствующих правилам свёртки; причём колонки, участвующие в суммиро-
//           вании (т.е. из рПоляСуммы) также в исходном, непросуммированном виде присутствуют в этих вложеных подтаблицах.
//
Функция СвернутьТаблицу(Знач рТаблица,Знач рПоляСвертки,Знач рПоляСуммы,рИмяКолонкиАгрегатора="") Экспорт
Попытка
   
мПоляСвертки=ОбщегоНазначения.РазложитьСтрокуВМассивПодстрок(рПоляСвертки,",");
   
мПоляСуммы=ОбщегоНазначения.РазложитьСтрокуВМассивПодстрок(рПоляСуммы,",");
    Если
ТипЗнч(рТаблица)<>Тип("ТаблицаЗначений") Тогда Возврат рТаблица КонецЕсли;
    Если
рТаблица.Колонки.Количество()=0 Тогда Возврат рТаблица КонецЕсли;

    Если
мПоляСвертки.Количество()=0 и мПоляСуммы.Количество()=0 Тогда
       
// определяем по типам значений
       
рПоляСуммы=""; разд="";
        Для каждого
кол Из рТаблица.Колонки Цикл
            Если
кол.ТипЗначения.СодержитТип(Тип("Число")) Тогда
               
мПоляСуммы.Добавить(СокрЛП(кол.Имя));
               
рПоляСуммы=рПоляСуммы+разд+СокрЛП(кол.Имя); разд=",";
            КонецЕсли;
        КонецЦикла;
        Если
мПоляСуммы.Количество()=0 Тогда // ничего поделать нельзя
           
Возврат рТаблица;
        КонецЕсли;
    КонецЕсли;

    Если
мПоляСвертки.Количество()=0 и мПоляСуммы.Количество()<>0 Тогда
       
// все поля, не входящие в суммируемые, подразумеваются как свёрточные
       
рПоляСвертки=""; разд="";
        Для каждого
кол Из рТаблица.Колонки Цикл
            Если
мПоляСуммы.Найти(СокрЛП(кол.Имя))=Неопределено Тогда
               
мПоляСвертки.Добавить(СокрЛП(кол.Имя));
               
рПоляСвертки=рПоляСвертки+разд+СокрЛП(кол.Имя); разд=",";
            КонецЕсли;
        КонецЦикла;
    КонецЕсли;

    Если
ПустаяСтрока(рИмяКолонкиАгрегатора) Тогда // режим агрегирования не используется
       
рТаблица.Свернуть(рПоляСвертки,рПоляСуммы);
    КонецЕсли;

    Если
ПустаяСтрока(рПоляСвертки)
    или
мПоляСвертки.Количество()=0
   
или ПустаяСтрока(рИмяКолонкиАгрегатора)
    Тогда Возврат
рТаблица КонецЕсли;

   
рСКД=Новый СхемаКомпоновкиДанных;
   
//
   
рИсточникДанных=рСКД.ИсточникиДанных.Добавить();
   
рИсточникДанных.Имя="ОсновнойИсточник";
   
рИсточникДанных.ТипИсточникаДанных="Local";
   
//
   
рНабор=рСКД.НаборыДанных.Добавить(Тип("НаборДанныхОбъектСхемыКомпоновкиДанных"));
   
рНабор.Имя="ОсновнойНабор";
   
рНабор.ИмяОбъекта="ТаблицаИсточник"; // связывание с внешними данными идёт именно по нему
   
рНабор.ИсточникДанных="ОсновнойИсточник";
   
//
   
рНастройка=рСКД.НастройкиПоУмолчанию;
   
рГруппировкаКД=рНастройка.Структура.Добавить(Тип("ГруппировкаКомпоновкиДанных"));
    Для каждого
рИмяПоляСвертки Из мПоляСвертки Цикл
       
рПолеГр=рГруппировкаКД.ПоляГруппировки.Элементы.Добавить(Тип("ПолеГруппировкиКомпоновкиДанных"));
       
рПолеГр.Поле=Новый ПолеКомпоновкиДанных(СокрЛП(рИмяПоляСвертки));
       
рПолеГр.ТипГруппировки=ТипГруппировкиКомпоновкиДанных.Элементы;
    КонецЦикла;
   
рГруппировкаКД.Выбор.Элементы.Добавить(Тип("АвтоВыбранноеПолеКомпоновкиДанных"));
   
//
   
мОстающихся=Новый Массив;
   
стрОстающихся=""; разд="";
    Для каждого
кол Из рТаблица.Колонки Цикл
       
#Если Клиент Тогда
           
ОбработкаПрерыванияПользователя();
       
#КонецЕсли
       
// вносим поля в набор, это обязательно
       
рПоле=рНабор.Поля.Добавить(Тип("ПолеНабораДанныхСхемыКомпоновкиДанных"));
       
рПоле.Заголовок=кол.Заголовок;
       
рПоле.Поле=кол.Имя;
       
рПоле.ПутьКДанным=кол.Имя;
       
рПоле.ТипЗначения=СКДиПостроители.ПолучитьОписаниеТипаБезПустых(кол.ТипЗначения);
       
// выбранные поля добавляем на уровень самой настройки, т.е. группы "Отчёт"
       
рВыбПоле=рНастройка.Выбор.Элементы.Добавить(Тип("ВыбранноеПолеКомпоновкиДанных"));
       
рВыбПоле.Заголовок=рПоле.Заголовок;
       
рВыбПоле.Поле=Новый ПолеКомпоновкиДанных(рПоле.ПутьКДанным);
       
// выясним, куда попадает эта колонка
       
Если мПоляСвертки.Найти(СокрЛП(кол.Имя))=Неопределено Тогда // она может быть и сумматорной
           
мОстающихся.Добавить(кол.Имя);
           
стрОстающихся=стрОстающихся+разд+кол.Имя; разд=",";
        КонецЕсли;
    КонецЦикла;
    Если
ПустаяСтрока(стрОстающихся) Тогда // вообще ничего, по сути, не надо
       
рТаблица.Свернуть(СокрЛП(рПоляСвертки));
        Возврат
рТаблица;
    КонецЕсли;

   
// вносим само поле-вычислитель
   
рВыражение="ВЫБОР КОГДА Уровень()=0 ТОГДА ВычислитьВыражение(""ТаблицаЗначений("+стрОстающихся+")"",,,""Текущая"",""Последняя"") КОНЕЦ";
   
рВычПоле=рСКД.ВычисляемыеПоля.Добавить();
   
рВычПоле.ПутьКДанным=СокрЛП(рИмяКолонкиАгрегатора);
   
рВычПоле.Заголовок="Таблицы";
   
рВычПоле.Выражение=рВыражение;
   
//
   
рИтПоле=рСКД.ПоляИтога.Добавить();
   
рИтПоле.Выражение=СокрЛП(рИмяКолонкиАгрегатора);
    Для каждого
рИмяПоляСвертки Из мПоляСвертки Цикл
       
рИтПоле.Группировки.Добавить(СокрЛП(рИмяПоляСвертки));
    КонецЦикла;
   
рИтПоле.ПутьКДанным=СокрЛП(рИмяКолонкиАгрегатора);
   
//
   
Для каждого рИмяПоляСуммы Из мПоляСуммы Цикл
       
рИтПоле=рСКД.ПоляИтога.Добавить();
       
рИтПоле.Выражение="Сумма("+СокрЛП(рИмяПоляСуммы)+")";
       
рИтПоле.ПутьКДанным=СокрЛП(рИмяПоляСуммы);
       
// а группировки оставим пустыми, чтобы шёл по всем
   
КонецЦикла;
   
//
   
рВыбПоле=рНастройка.Выбор.Элементы.Добавить(Тип("ВыбранноеПолеКомпоновкиДанных"));
   
рВыбПоле.Заголовок="Таблицы";
   
рВыбПоле.Поле=Новый ПолеКомпоновкиДанных(рВычПоле.ПутьКДанным);

   
рКомпоновщикН=Новый КомпоновщикНастроекКомпоновкиДанных;
   
рКомпоновщикН.Инициализировать(Новый ИсточникДоступныхНастроекКомпоновкиДанных(рСКД));
   
рКомпоновщикН.ЗагрузитьНастройки(рНастройка);

   
резТаблица=СКДиПостроители.ПоместитьРезультатСКДвТаблицуЗначений(рсКД,рКомпоновщикН,рТаблица);
    Если
резТаблица.Количество()<>0 Тогда
       
послстро=резТаблица[резТаблица.Количество()-1];
        Если
ТипЗнч(послстро[СокрЛП(рИмяКолонкиАгрегатора)])<>Тип("ТаблицаЗначений") Тогда
           
резТаблица.Удалить(послстро);
        КонецЕсли;
    КонецЕсли;
   
//
   
Возврат резТаблица;
Исключение
   
Сообщить("СвернутьТаблицу, ошибка: "+ОписаниеОшибки(),СтатусСообщения.ОченьВажное);
    Возврат
рТаблица;
КонецПопытки;
КонецФункции

 

Внимание! Если у кого-то не заработает или будет работать неверно - пожалуйста, сообщите, постараюсь оперативно разобраться.

Пы.Сы. Не уверен, что за такую фиговинку имеет смысл плюсить.

 

 

Вступайте в нашу телеграмм-группу Инфостарт

См. также

Загрузка и выгрузка в Excel Универсальные функции Программист 1С:Предприятие 8 Россия Бесплатно (free)

Описанный ниже подход позволяет в три шага заполнять формулы в Excel файлы, вне зависимости от ОС сервера (MS Windows Server или Linux). Подход подразумевает отказ от работы с COM-объектом в пользу работы через "объектную модель документа" (DOM).

30.10.2025    3540    Abysswalker    9    

45

Универсальные функции Работа с интерфейсом Программист 1С:Предприятие 8 Бесплатно (free)

Порой необходимо временно отключить расширение 1С, не удаляя его, чтобы не потерять данные. Но в этом случае при каждом запуске всем будет лезть уведомление о неактивном расширении, хотя очевидно, это техническая информация, которой не стоит лишний раз пугать пользователей.

14.05.2025    6536    DeerCven    15    

57

Универсальные функции Программист 1С:Предприятие 8 1C:Бухгалтерия Бесплатно (free)

Благодаря этим пяти строчкам можно больше не заморачиваться с загрузкой из внешних файлов. Пользуюсь везде, всегда и постоянно.

21.05.2024    49183    dimanich70    83    

170

Универсальные функции Программист 1С:Предприятие 8 1C:Бухгалтерия Абонемент ($m)

Задача: вставить картинку из буфера обмена на форму средствами платформы 1С.

1 стартмани

18.03.2024    7366    6    John_d    13    

59

Универсальные функции Программист Стажер 1С:Предприятие 8 1C:Бухгалтерия Бесплатно (free)

Пришлось помучиться с GUID-ами немного, решил поделиться опытом, мало ли кому пригодится.

12.02.2024    61649    atdonya    31    

70

Универсальные функции Программист 1С:Предприятие 8 Бесплатно (free)

На заключительных этапах, когда идет отладка или доработка интерфейса, необходимо много раз переоткрыть внешний объект. Вот один из способов автоматизации этого.

30.11.2023    9159    ke.92@mail.ru    17    

68
Комментарии
Подписаться на ответы Инфостарт бот Сортировка: Древо развёрнутое
Свернуть все
1. AnryMc 850 01.07.13 12:51 Сейчас в теме
А зачем? Всегда можно оперировать копией ТЗ.
2. Yashazz 4892 01.07.13 13:56 Сейчас в теме
Можно. Только копия ресурс кушает, если большая, и "оперировать", т.е. искать даже по индексированной - иногда скучное дело. Я всего лишь показал альтернативу, когда "всё в одном". ))
3. EarlyBird 7 01.07.13 20:43 Сейчас в теме
жениться тебе надо
тогда будет жаль времени на всякую ненужную ботву
vvlas; Yan_Malyakov; +2 Ответить
6. Yashazz 4892 02.07.13 12:04 Сейчас в теме
(3) Позвольте представить, моя жена: http://infostart.ru/community/profile/41675/ - и уже восемь лет вместе )))
(5) Смысл простой - и свернуть надо, и данные не потерять. А список или массив хороши, когда такая колонка одна. Если было 10 колонок, по 1-й и 3-й свернули, пятую суммировали, то, когда нужны все остальные, подтаблица удобнее списка.
smirnov.a; +1 Ответить
4. Гость 02.07.13 03:28
Как пример простой программной работы с СКД очень хорошо. Стоило преподносить публикацию именно как пример работы с СКД, тогда польза от нее очевидна.
5. batlerrett 02.07.13 07:51 Сейчас в теме
Смысл тогда сворачивать? Если нужны списки, так и надо в 3-й колонке значением список делать
7. EarlyBird 7 02.07.13 12:25 Сейчас в теме
честно говоря, пока для себя не вижу практического применения данного решения
если я что-то сворачиваю, я это сворачиваю. Если не надо сворачивать, я не сворачиваю.
Но всё же плюс, за конструктивные ответы (и красивую жену :) )
9. Yashazz 4892 02.07.13 13:41 Сейчас в теме
(7) Мне часто приходится сталкиваться, раньше делал через 2 таблицы (свёрнутая и исходная), но оперативку жалко. А так пыхтит сервер приложения, насколько я понял распределение нагрузки. И что-то мне подсказывает, что напрячь механизм СКД в этом случае более экономно, нежели крутить временные таблицы.

А если честно, просто захотелось побаловаться с этими функциями СКД, и всё ))
8. Const1C 02.07.13 13:01 Сейчас в теме
В семерки часто сталкиваюсь с такой задачей. Что нужно данные сохранять, после свертки. В частности при формировании группировок в отчетах. Но, ИМХО, в такой ситуации, лучше, действительно, оперировать таблицей значений. А уж если ресурсы жалко (когда, например, нужно работать с 100500 записями). Лучше использовать временные таблицы.
10. TrinitronOTV 16 04.07.13 17:18 Сейчас в теме
не смотря на комментарии, для меня всё равно полезно было ознакомиться, спасибо
11. AnryMc 850 04.07.13 21:48 Сейчас в теме
(10) TrinitronOTV, Кто к нам пожаловал...
12. Збянтэжаны Саўка 245 05.07.13 09:39 Сейчас в теме
(0) СКДиПостроители - это откуда? не встречал такого модуля.
13. sstar90 05.07.13 10:18 Сейчас в теме
(0) Присоединяюсь к (12)
Если это твой модуль, было бы логично выложить и его (в части, касающейся данного решения).
14. Yashazz 4892 05.07.13 12:00 Сейчас в теме
Ёлы-палы, сколько ж я ещё на эти грабли наступать буду... Ща, выложу нужные функции.
15. Збянтэжаны Саўка 245 05.07.13 14:24 Сейчас в теме
ок, ПоместитьРезультатСКДвТаблицуЗначений() уже есть, но нужна еще ПолучитьОписаниеТипаБезПустых()
16. Yashazz 4892 05.07.13 21:40 Сейчас в теме
(15) Она совсем простая и необязательная:
Функция ПолучитьОписаниеТипаБезПустых(рОписТипов) Экспорт
мТипов=Новый Массив;
Для каждого рТип Из рОписТипов.Типы() Цикл
Если рТип=Тип("Неопределено") или рТип=Тип("NULL") или рТип=Неопределено или рТип=Null Тогда Продолжить КонецЕсли;
мТипов.Добавить(рТип);
КонецЦикла;
Возврат Новый ОписаниеТипов(мТипов,рОписТипов.КвалификаторыЧисла,рОписТипов.КвалификаторыСтроки,рОписТипов.КвалификаторыДаты);
КонецФункции
17. Abadonna 3976 09.07.13 17:08 Сейчас в теме
(0)
Пы.Сы. Не уверен, что за такую фиговинку имеет смысл плюсить.

Вот именно за это плюс и поставлю ;)
18. chmv 10.07.13 13:36 Сейчас в теме
Для отправки сообщения требуется регистрация/авторизация