Кросс-таблица из табличной части или запроса

27.09.16

Разработка - Запросы

Данная задача встречалась мне несколько раз. Каждый раз в новой интерпретации, но суть примерно одинаковая: "Хочу, чтобы по строкам были товары, по колонкам склады, а на пересечении - количество". Вместо товары-склады, может быть что угодно: дата-контрагент, номенклатура+цвет - размер и т.д.

Скачать файл

ВНИМАНИЕ: Файлы из Базы знаний - это исходный код разработки. Это примеры решения задач, шаблоны, заготовки, "строительные материалы" для учетной системы. Файлы ориентированы на специалистов 1С, которые могут разобраться в коде и оптимизировать программу для запуска в базе данных. Гарантии работоспособности нет. Возврата нет. Технической поддержки нет.

Наименование По подписке [?] Купить один файл
Пример Кросс-таблица (ОФ, УФ, 8.3)
.epf 20,23Kb
126
126 Скачать (1 SM) Купить за 1 850 руб.

Для примера возьмем некоторый документ с табличной частью Остатки на конец смены. Структура полей табличной части представлена на рисунке справа.

Заказчику неудобно работать с ней в таком виде, и он желает просматривать ее и редактировать в виде кросс-таблицы, как на рисунке слева.
Замечание: на картинке приводится две таблицы одновременно для наглядности. В реальных задачах обычно требуется отображать только кросс-таблицу.

Основная сложность в сопоставлении колонок кросс-таблицы с соответствующими строками табличной части. Ниже представлены три процедуры, которые решают данную задачу за несколько строк кода. Вот пример их использования:

Процедура ТранспорироватьТабличнуюЧастьВКроссТаблицу()
	
	// 1. Рассчитаем необходимое количество колонок в новой таблице значений
	ТаблицаСоответствий = ПолучитьИдентификаторыДанных(
					ОстаткиНаКонецСмены.Выгрузить(),        // исходная таблица значений
					"Стеллаж");                             // поля вертикальной группировки через запятую
	ОстаткиНаКонецСмены_Служебная.Загрузить(ТаблицаСоответствий);
	Для Каждого Строка Из ОстаткиНаКонецСмены_Служебная Цикл
		Строка.Заголовок = Строка.Стеллаж; // заголовок колонки при разворачивании
		Строка.Тип = Новый ОписаниеТипов("Число"); 
	КонецЦикла;
	
	// 2. Получим транспорированную таблицу
	КроссТаблица = ТрансформироватьДанныеВКроссТаблицу(
					ОстаткиНаКонецСмены.Выгрузить(),           // исходная таблица значений
					ОстаткиНаКонецСмены_Служебная.Выгрузить(), // служебная таблица значений
					"Номенклатура",         // поля горизонтальной группировки через запятую
					"Количество");          // поле, из которого будет подставляться значение
					                        // на пересечении вертикальных и горизонтальных группировок
	
КонецПроцедуры

Процедура ТранспорироватьКроссТаблицуВТабличнуюЧасть()
	
	// Обратная трансформация
	ТабличнаяЧасть = ТрансформироватьКроссТаблицуВДанные(
					ОстаткиНаКонецСмены_КроссТаблица.Выгрузить(), 
					ОстаткиНаКонецСмены_Служебная.Выгрузить(), 
					"Количество");			// поле, в которое будут записываться значения
					              			// из пересечения вертикальных и горизонтальных группировок
	ОстаткиНаКонецСмены.Загрузить(ТабличнаяЧасть);
	
КонецПроцедуры

Небольшие пояснения:
1. Дополнительная информация для связи между исходной и конечной таблицами хранится в служебной таблице. Ее необходимо сохранять на протяжении всей работы с кросс-таблицей. Ее структура простая: Имя, Заголовок и Тип колонок, а также имена полей табличной части, по сочетанию которых будут создаваться колонки. В моем примере это одно поле “Стеллаж”, но их может быть и несколько, например “Номенклатура,Характеристика”. Для рассматриваемого примера структура служебной таблицы с данными представлена на рисунке

 

2. Прямое преобразование состоит из двух шагов: 
     - генерируем идентификаторы колонок для кросс-таблицы, заполняем заголовки и типы дополнительных полей. В моем примере в заголовок я пишу наименование стеллажа, тип на пересечении горизонтальных группировок - числовой.
     - получаем кросс-таблицу

Сами процедуры
// Пусть нужно по некоторой таблице значений создать другую таблицу значений
// При этом колонки второй таблицы соответствуют сочетанию полей в первой таблице 
// (например, в первой таблице одна колонка Номенклатура, а во второй должна быть отдельная колонка для каждой номенклатуры)
// Тогда первую таблицу будем для краткости называть ДАННЫЕ, а вторрую КРОССТАБЛИЦА

// для решения этой задачи потребуется дополнительная таблица, которую будем назывть
// ИДЕНТИФИКАТОРЫКОЛОНОК

// Данный алгоритм выполняет преобразования 
//		- ДАННЫЕ (1)-> ИДЕНТИФИКАТОРЫКОЛОНОК (2)-> КРОССТАБЛИЦА
//		- КРОССТАБЛИЦА (3)-> ДАННЫЕ

Функция ПолучитьИдентификаторыДанных(тДанные, стрПоляВертикальныхГруппировок) Экспорт
	
	тИдентификаторыКолонок = тДанные.Скопировать(,стрПоляВертикальныхГруппировок);
	тИдентификаторыКолонок.Свернуть(стрПоляВертикальныхГруппировок);
	
	тИдентификаторыКолонок.Колонки.Добавить("Имя");
	тИдентификаторыКолонок.Колонки.Добавить("Заголовок");
	тИдентификаторыКолонок.Колонки.Добавить("Тип");
	
	Для сч = 1 по тИдентификаторыКолонок.Количество() Цикл
		тИдентификаторыКолонок[сч-1].Имя = "Группировка_" + Формат(сч, "ЧЦ=5; ЧВН=; ЧГ=0");
	КонецЦикла;
	
	Возврат тИдентификаторыКолонок;
	
КонецФункции

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

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

Отмечу также, что данные методы можно использовать не только для разворачивания табличной части, но и таблицы из запроса (например, в АРМе)

Процедуры тестировались на платформе 8.3 в режиме совместимости с 8.2. Однако скорее всего будут работать и на платформе 8.1

Прикладываю работающий пример для обычных и управляемых форм. Отмечу, что для обычных форм задача намеренно несколько усложнена - в кросс-таблицу добавлена служебная колонка с суммой по строке, а также выводятся все стеллажи, которые связаны со складом, в том числе и те, которых нет в табличной части.

Процедуры кросс-таблица транспорировать транспорирование

См. также

Инструментарий разработчика Роли и права Запросы СКД Программист Руководитель проекта Платформа 1С v8.3 Управляемые формы Запросы Система компоновки данных Платные (руб)

Инструменты для разработчиков 1С 8.3: Infostart Toolkit. Автоматизация и ускорение разработки на управляемых формах. Легкость работы с 1С.

15500 руб.

02.09.2020    185835    1036    403    

971

Обновление 1С Запросы Программист Платформа 1С v8.3 1С:ERP Управление предприятием 2 Абонемент ($m)

Данный инструмент помогает анализировать доработанную конфигурацию после обновления на новый релиз и находить «битые» тексты запросов, в которых участвуют несуществующие в новом релизе метаданные.

2 стартмани

06.02.2025    2255    17    XilDen    26    

36

Запросы Программист Платформа 1С v8.3 Запросы 1C:Бухгалтерия Бесплатно (free)

В статье приведена удобная возможность отладки исполняемого запроса динамического списка.

03.12.2024    5861    artemusII    11    

23

Запросы Программист Бесплатно (free)

Увидел cheatsheet по SQL и захотелось нарисовать подобное, но про запросы.

18.10.2024    13264    sergey279    18    

66

Запросы Программист Платформа 1С v8.3 Запросы 1C:Бухгалтерия Бесплатно (free)

Столкнулся с интересной ситуацией, которую хотел бы разобрать, ввиду её неочевидности. Речь пойдёт про использование функции запроса АВТОНОМЕРЗАПИСИ() и проблемы, которые могут возникнуть.

11.10.2024    8333    XilDen    36    

90

СКД Механизмы типовых конфигураций Запросы Программист Платформа 1С v8.3 1С:Зарплата и кадры государственного учреждения 3 1С:Зарплата и Управление Персоналом 3.x Россия Бесплатно (free)

Работая с типовыми отчетами в конфигурациях «Зарплата и управление персоналом, редакция 3», «Зарплата и кадры государственного учреждения, редакция 3» и подобных, в схемах компоновки данных можно встретить конструкции запросов, которые обращаются к некоторым виртуальным таблицам.

20.08.2024    3263    PROSTO-1C    0    

23

Запросы Программист Запросы Бесплатно (free)

Отлаживая взаимодействие с базой данных, мы регулярно сталкиваемся с зависающими или подозрительно долго выполняющимися обращениями, негативно влияющими на производительность. О том, как в PostgreSQL выявить подозрительные запросы, основываясь на доступной о них информации, расскажем в статье.

16.08.2024    10905    user1840182    5    

29
Комментарии
Подписаться на ответы Инфостарт бот Сортировка: Древо развёрнутое
Свернуть все
1. Hatson 537 19.09.16 11:43 Сейчас в теме
Хм, ну надо же))
А через неделю я опубликовал свою статью про СКД.

http://infostart.ru/public/549297/

Задам провокационный вопрос:
А что делать если в одной кросс-таблице нужно свести данные из нескольких реальных таблиц (табличных частей) с разной детализацией?
2. json 3373 19.09.16 23:30 Сейчас в теме
(1) Hatson, оба способа имеют право на существование. Все зависит от предпочтений программиста и требований клиента.
Я изначально тоже хотел сделать через СКД, когда у меня встала такая задача. Но ключевому пользователю показалось неудобно редактировать в табличном документе. В итоге родился данный способ, который был уже применен несколько раз мной и коллегами.

По поводу твоего вопроса - да можно сделать, например вывести учетный остаток по складу в моем примере. Также приходилось делать и группировки. Все это ложится в данную схему при желании. Кроме того, хитрые соединения при редактировании табличной части - не такое уж и частое явление. В большинстве случаев достаточно просто построить кросс-таблицу

В схеме с СКД экономятся силы программиста на выводе и тратятся на редактировании. В схеме с табличным полем - наоборот. Все равно думать надо и в том и другом случае.
3. topasha 23 13.10.16 00:45 Сейчас в теме
Вот спасибо, хороший человек! Кучу времени мне сэкономил.
4. user702069_savel777v2 03.03.17 15:35 Сейчас в теме
Столкнулся с аналогичной задачей при разработке нового документа. В первой ТЧ документа Электроприборы, по второй их лимиты в разрезе Периодов. Весь код yurii_host "тащить" постеснялся, реализовал свою реализацию с вашей изначальной идеей. Т.е. КроссТаблица + одна вспомогательная таблица которая хранит соответствие номеров колонок и периодов.
всё бы хорошо... но вот когда в документ понадобилось ещё и итоги по колонкам добавить..... тут я встрял...
В 8.3 итогов у ТаблицаЗначений нет, а значит + ещё куча обработчиков...
потому посчитал проще переделать на СКД (по типу Hatson) + обработчики расшифровки...
Потому могут предметно говорить о выгодах того или иного варианта:
1 Таблица хорошо, когда ресурсов немного, нет требований к оформлению, т. е. когда задача условно мала. Тогда "ваять" СКД + обработчики будет затратнее...
Во всех остальных случаях лучше СКД, преимуществ немало:
1. Простота обслуживания. после итогов внес ещё не мало правок по форме вывода информации, заголовков, выводимых итогов и т.д. Даже не представляю как я это делал бы на таблице.
2. Переносил тот же код на другие документы с теми же задачами... СКД переносится легче, меньше реквизитов формы и процедур обработчиков.
3. СКД работает заметно быстрее при перемещениях по таблице (видно даже субъективно для отладки юзал одни и те же доки двумя разными формами)...
В целом рекомендую делать на СКД...
П,С. Обоим авторам благодарность на красивые идеи!
5. andrey80nik 6 14.06.23 11:20 Сейчас в теме
Намекните какую функцию надо поправить чтобы можно было колонки произвольно добавлять сразу в кросс таблицу?
6. vik2006 142 03.10.23 04:56 Сейчас в теме
(5) Да, присоединяюсь к вопросу. А как быть, если нужно несколько колонок добавить, например, Количество, Цена и Сумма?
7. vik2006 142 15.11.23 03:08 Сейчас в теме
Доброго времени суток, автор. А как быть, если нужно несколько колонок добавить, например, Количество, Цена и Сумма?
Оставьте свое сообщение