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

27.09.16

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

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

Скачать исходный код

Наименование Файл Версия Размер
Пример Кросс-таблица (ОФ, УФ, 8.3)
.epf 20,23Kb
118
.epf 20,23Kb 118 Скачать

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

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

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

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

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

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

 

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

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

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

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

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

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

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

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

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

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

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

См. также

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

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

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

10000 руб.

02.09.2020    129742    700    390    

752

Как посмотреть итоговый запрос в отчете СКД

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

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

15.05.2024    2953    implecs_team    4    

34

Пропорциональное распределение в запросе с использованием АвтоНомерЗаписи()

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

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

11.04.2024    2619    andrey_sag    10    

32

Для чего используют конструкцию запроса "ГДЕ ЛОЖЬ" в СКД на примере конфигурации 1С:ERP

Запросы СКД Платформа 1С v8.3 Запросы Система компоновки данных 1С:ERP Управление предприятием 2 Бесплатно (free)

В типовых конфигурациях разработчики компании 1С иногда используют в отчетах, построенных на СКД, такую конструкцию, как "ГДЕ ЛОЖЬ". Такая конструкция говорит о том, что данные в запросе не будут получены совсем. Для чего же нужен тогда запрос?

13.02.2024    6300    KawaNoNeko    23    

26

Набор-объект для СКД по тексту или запросу

Запросы СКД Платформа 1С v8.3 Управляемые формы Конфигурации 1cv8 Абонемент ($m)

Есть список полей в виде текста, или запрос - закидываем в набор СКД.

1 стартмани

31.01.2024    2291    2    Yashazz    0    

32

Запрос 1С copilot

Инструментарий разработчика Запросы Платформа 1С v8.3 Управляемые формы Конфигурации 1cv8 Бесплатно (free)

Пишем на человеческом языке, что нам надо, и получаем текст запроса на языке 1С. Используются большие языковые модели (LLM GPT) от OpenAI или Яндекс на выбор.

15.01.2024    7115    40    mkalimulin    32    

53

PrintWizard: поддержка представлений ЗУП в конструкторе

Инструментарий разработчика Запросы Платформа 1С v8.3 Бесплатно (free)

Одной из интересных задач, стоящих в процессе разработки, была поддержка механизма представлений в ЗУП. Но не просто возможность исполнения запросов с ними. Основная проблема была в том, чтобы с ними было удобно работать, а именно: создавать, модифицировать и отлаживать. Кратко о том, что в итоге получилось...

14.12.2023    2015    vandalsvq    7    

29

Консоль запросов УФ 8.3.2.24.12 (мод от Dr.Zombi)

Инструментарий разработчика Запросы Платформа 1С v8.3 Управляемые формы Запросы Россия Абонемент ($m)

Работа с запросом и СКД, Полная поддержка пакетных запросов, временных таблиц. Главное скорость отладки запроса и данных, а красота вторична.

1 стартмани

07.12.2023    3483    52    DrZombi    54    

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

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

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