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

27.09.16

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

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

Скачать файл

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

Наименование По подписке [?] Купить один файл
Пример Кросс-таблица (ОФ, УФ, 8.3)
.epf 20,23Kb
123
123 Скачать (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

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

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

См. также

SALE! %

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

Набор инструментов программиста и специалиста 1С для всех конфигураций на управляемых формах. В состав входят инструменты: Консоль запросов, Консоль СКД, Консоль кода, Редактор объекта, Анализ прав доступа, Метаданные, Поиск ссылок, Сравнение объектов, Все функции, Подписки на события и др. Редактор запросов и кода с раскраской и контекстной подсказкой. Доработанный конструктор запросов тонкого клиента. Продукт хорошо оптимизирован и обладает самым широким функционалом среди всех инструментов, представленных на рынке.

12000 10000 руб.

02.09.2020    165844    921    403    

894

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

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

18.10.2024    10931    sergey279    18    

65

Запросы Программист Платформа 1С v8.3 Запросы Конфигурации 1cv8 Бесплатно (free)

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

11.10.2024    6014    XilDen    36    

81

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

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

16.08.2024    8676    user1840182    5    

28

Математика и алгоритмы Запросы Программист Платформа 1С v8.3 Запросы Бесплатно (free)

Рассмотрим быстрый алгоритм поиска дублей с использованием hash функции по набору полей шапки и табличных частей.

08.07.2024    2628    ivanov660    9    

22

Запросы СКД Программист Стажер Система компоновки данных Россия Бесплатно (free)

Часто при разработке отчетов в СКД возникает ситуация, когда не совсем понятно, почему отчет выводит не те данные, которые нужны, либо не выводит вовсе. Возникает потребность увидеть конечный запрос, который формирует СКД. Как это сделать, рассмотрим в этой статье.

15.05.2024    9795    implecs_team    6    

48

Запросы Программист Стажер Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

Часто поступают задачи по произвольному распределению общих сумм. После распределения иногда пропадают копейки. Суть решения добавить АвтоНомерЗаписи() в ВТ распределения, и далее используя функции МАКСИМУМ или МИНИМУМ можем положить разницу копеек в первую или последнюю строку знаменателя распределения.

11.04.2024    3542    andrey_sag    10    

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

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

Задам провокационный вопрос:
А что делать если в одной кросс-таблице нужно свести данные из нескольких реальных таблиц (табличных частей) с разной детализацией?
2. json 3353 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 137 03.10.23 04:56 Сейчас в теме
(5) Да, присоединяюсь к вопросу. А как быть, если нужно несколько колонок добавить, например, Количество, Цена и Сумма?
7. vik2006 137 15.11.23 03:08 Сейчас в теме
Доброго времени суток, автор. А как быть, если нужно несколько колонок добавить, например, Количество, Цена и Сумма?
Оставьте свое сообщение