Как вывести несколько записей в кросс-таблице СКД без схлопывания строк?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При разработке отчетов на системе компоновки данных (СКД) программисты часто сталкиваются со специфической проблемой при выводе данных в кросс-таблицу. Если на пересечении строки и колонки оказывается несколько различных записей, СКД по умолчанию пытается их сагрегировать (например, выбирая максимум или минимум) либо выводит детальные записи, что полностью ломает и раздувает структуру отчета. Типичный пример — вывод графика поставок, где один и тот же контрагент в один и тот же день может иметь несколько разных временных интервалов доставки.

В этой статье мы подробно разберем, почему возникает этот эффект, и рассмотрим два профессиональных метода решения этой задачи: классический (с помощью нумерации строк внутри группировок в запросе) и современный (через агрегацию строк в ресурсах СКД).

Выясняем причину: почему СКД схлопывает строки?

Проанализируем стандартное поведение кросс-таблицы. Представим, что у нас есть независимый регистр сведений со следующими измерениями: Номенклатура (СправочникСсылка), ДеньПоставки (ПеречислениеСсылка) и ВремяПоставки (ПеречислениеСсылка). Нам необходимо получить таблицу, где строками будет выступать номенклатура, колонками — дни недели, а в ячейках на пересечении должно отображаться время.

Если для одного товара на один день приходится две разные поставки (например, «Товар 1» в «День 2» должен приехать в 16:00 и в 17:00), то при обычной настройке таблицы СКД предложит нам сделать поле ВремяПоставки ресурсом. Если мы укажем выражение ресурса Максимум(ВремяПоставки), система выведет только одну запись (17:00), а запись 16:00 будет безвозвратно утеряна (схлопнута). Если же мы попытаемся выводить детальные записи вместо ресурсов, отчет «разъедется» — под каждую запись будет создаваться отдельная строка в таблице, что сделает отчет нечитаемым.

Для решения этой проблемы нам нужно либо искусственно разделить строки внутри группировки, либо объединить несколько значений внутри одной ячейки. Рассмотрим оба подхода по шагам.

Способ 1. Нумерация записей внутри группировок через самосоединение в запросе

Этот метод основан на классическом приеме реляционных баз данных. Наша задача — пронумеровать строки по порядку для каждого уникального сочетания полей Номенклатура и ДеньПоставки. В результате «Товар 1» в «День 2» со временем 16:00 получит номер строки 1, а со временем 17:00 — номер строки 2. Благодаря этому СКД сможет сопоставить их и вывести на разных строках внутри одной группировки номенклатуры.

Выполним нумерацию строк с помощью внутреннего соединения таблицы самой с собой. Разберем структуру запроса:


ВЫБРАТЬ
	Т1.Номенклатура КАК Номенклатура,
	Т1.ДеньПоставки КАК ДеньПоставки,
	Т1.ВремяПоставки КАК ВремяПоставки,
	КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Т2.ВремяПоставки) КАК НомерСтроки
ИЗ
	РегистрСведений.Поставки КАК Т1
		ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.Поставки КАК Т2
			ПО Т1.Номенклатура = Т2.Номенклатура
				И Т1.ДеньПоставки = Т2.ДеньПоставки
				И Т1.ВремяПоставки >= Т2.ВремяПоставки
ГРУППИРОВАТЬ ПО
	Т1.Номенклатура,
	Т1.ДеньПоставки,
	Т1.ВремяПоставки

Рассмотрим подробнее, как работает это соединение:

  1. Мы связываем таблицу саму с собой по ключевым полям Номенклатура и ДеньПоставки.
  2. Условие Т1.ВремяПоставки >= Т2.ВремяПоставки позволяет нам для каждого конкретного времени посчитать, сколько записей в этой же группе имеют время, меньшее или равное текущему.
  3. Функция КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Т2.ВремяПоставки) превращает это сопоставление в порядковый номер (1, 2, 3 и т.д.).

Важное предупреждение по производительности: Использование неявного соединения с условием «больше или равно» (>=) на реальных таблицах с большим объемом данных (десятки тысяч записей и более) может привести к критическому падению скорости выполнения запроса на СУБД. Если ваш регистр содержит много записей, сначала отфильтруйте данные во временную таблицу по нужным параметрам отчета, проиндексируйте ее и только затем выполняйте самосоединение.

Проанализируем пример оптимизированного запроса с использованием временных таблиц:


ВЫБРАТЬ ПЕРВЫЕ 0
	ИсходныеДанные.Номенклатура КАК Номенклатура,
	ИсходныеДанные.ДеньПоставки КАК ДеньПоставки,
	ИсходныеДанные.ВремяПоставки КАК ВремяПоставки
ПОМЕСТИТЬ ВТ_Данные
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
	Т1.Номенклатура КАК Номенклатура,
	Т1.ДеньПоставки КАК ДеньПоставки,
	Т1.ВремяПоставки КАК ВремяПоставки,
	КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Т2.ВремяПоставки) КАК НомерСтроки
ИЗ
	ВТ_Данные КАК Т1
		ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_Данные КАК Т2
			ПО Т1.Номенклатура = Т2.Номенклатура
				И Т1.ДеньПоставки = Т2.ДеньПоставки
				И Т1.ВремяПоставки >= Т2.ВремяПоставки

ГРУППИРОВАТЬ ПО
	Т1.Номенклатура,
	Т1.ДеньПоставки,
	Т1.ВремяПоставки

После того как запрос подготовлен, перейдем к настройке схемы компоновки данных:

  1. Перейдем на вкладку «Ресурсы» и добавим поле ВремяПоставки. В качестве выражения укажем Максимум(ВремяПоставки).
  2. Перейдем на вкладку «Настройки» и добавим структуру типа «Таблица».
  3. В «Строки» таблицы добавим группировку по полю Номенклатура, а внутрь нее (вложенным элементом) добавим группировку по полю НомерСтроки.
  4. В «Колонки» таблицы добавим группировку по полю ДеньПоставки.
  5. В «Выбранные поля» настроек выведем ресурс ВремяПоставки.

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

Способ 2. Объединение строк внутри ячейки с помощью функций СКД (Современный метод)

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

Для реализации этого метода нам достаточно использовать самый простой запрос к регистру без каких-либо соединений:


ВЫБРАТЬ
	Поставки.Номенклатура КАК Номенклатура,
	Поставки.ДеньПоставки КАК ДеньПоставки,
	Поставки.ВремяПоставки КАК ВремяПоставки
ИЗ
	РегистрСведений.Поставки КАК Поставки

Вся магия настройки будет происходить непосредственно в конфигураторе СКД:

  1. Откроем вкладку «Ресурсы» схемы компоновки данных.
  2. Добавим поле ВремяПоставки в список ресурсов.
  3. В колонке «Выражение» напишем специальную агрегатную функцию СКД:
    СоединитьСтроки(Массив(Различные ВремяПоставки), Символы.ПС)
  4. Перейдем на вкладку «Настройки» и создадим структуру типа «Таблица».
  5. В «Строки» добавим группировку только по полю Номенклатура (без вложенных строк и номеров).
  6. В «Колонки» добавим группировку по полю ДеньПоставки.
  7. В «Выбранные поля» добавим наш ресурс ВремяПоставки.

Разберем подробнее, как работает выражение ресурса:

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

← На главную