При разработке отчетов на Системе Компоновки Данных (СКД) часто возникает задача объединить информацию из нескольких источников. Типичный пример — вывести в одной строке данные по номенклатуре из разных регистров накопления: остатки по заказам поставщикам, фактические остатки на складах, товары в резерве и так далее. Сложность в том, что нам нужно получить эти остатки не на одну общую дату, а на разные моменты времени, которые определяются документами-регистраторами в одном из регистров.
Рассмотрим ситуацию: есть основной регистр накопления, например, "ЗаказыПоставщикам". По одной номенклатуре в нем может быть несколько записей с разными датами. Задача — для каждой такой записи (и ее даты) получить остатки из других регистров, таких как "ТоварыНаСкладах" и "ТоварыВРезервеНаСкладах". Простая настройка связей между наборами данных в СКД часто приводит к "каше" в отчете и неверным результатам, особенно при добавлении третьего и последующих наборов данных. Проанализируем несколько подходов к решению этой задачи.
Это стандартный механизм, который СКД предлагает для соединения данных из разных источников. Идея в том, чтобы связать наборы данных и передать параметры из одного в другой. Разберем по шагам, как это должно работать.
РегистрНакопления.ЗаказыПоставщикам.ОстаткиИОбороты. Важно получить поля, по которым будет происходить связь (например, Номенклатура) и поле с датой (Период или Регистратор.Дата)."ТоварыНаСкладах", "ТоварыВРезерве") создайте отдельный набор данных-запрос. В качестве источника используйте виртуальную таблицу Остатки. В параметрах виртуальной таблицы укажите параметр для периода (например, &Период) и для отбора по номенклатуре.Номенклатура.Номенклатура.&Период).Источник.Период.Почему это может не работать? При большом количестве связей и сложных условиях СКД может формировать неоптимальный итоговый запрос. Для каждой строки основного набора будет выполняться подзапрос к дочернему, что сильно бьет по производительности. Также могут возникать проблемы с дублированием данных, если связи настроены не совсем корректно. Этот метод стоит пробовать первым, но если результат вас не устраивает, переходите к более надежным способам.
Этот подход считается наиболее стабильным, производительным и управляемым для сложных отчетов. Вся логика получения и соединения данных описывается в одном запросе с помощью временных таблиц. СКД получает на вход уже готовую плоскую таблицу со всеми необходимыми данными.
Шаг 1. Получение "опорных" точек (дат и номенклатуры)
Сначала нужно определить, на какие даты и по какой номенклатуре нам нужны срезы данных. Эту информацию мы берем из основного регистра и помещаем во временную таблицу.
ВЫБРАТЬ РАЗЛИЧНЫЕ
ЗаказыПоставщикамОбороты.Регистратор.Дата КАК Период,
ЗаказыПоставщикамОбороты.Номенклатура
ПОМЕСТИТЬ ВТ_ОпорныеТочки
ИЗ
РегистрНакопления.ЗаказыПоставщикам.Обороты(, , Регистратор, Номенклатура = &НужнаяНоменклатура) КАК ЗаказыПоставщикамОбороты
;
Шаг 2. Получение остатков из каждого регистра на опорные даты
Теперь для каждого дополнительного регистра мы получаем остатки на все даты из нашей временной таблицы ВТ_ОпорныеТочки. Для этого идеально подходит виртуальная таблица Остатки, которой в качестве параметра периода можно передать массив дат.
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ОстаткиНаСкладах.Период,
ОстаткиНаСкладах.Номенклатура,
ОстаткиНаСкладах.КоличествоОстаток КАК КоличествоНаСкладе
ПОМЕСТИТЬ ВТ_ОстаткиНаСкладах
ИЗ
РегистрНакопления.ТоварыНаСкладах.Остатки(
(ВЫБРАТЬ РАЗЛИЧНЫЕ Период ИЗ ВТ_ОпорныеТочки),
Номенклатура В (ВЫБРАТЬ РАЗЛИЧНЫЕ Номенклатура ИЗ ВТ_ОпорныеТочки)
) КАК ОстаткиНаСкладах
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
РезервыНаСкладах.Период,
РезервыНаСкладах.Номенклатура,
РезервыНаСкладах.КоличествоОстаток КАК КоличествоВРезерве
ПОМЕСТИТЬ ВТ_РезервыНаСкладах
ИЗ
РегистрНакопления.ТоварыВРезервеНаСкладах.Остатки(
(ВЫБРАТЬ РАЗЛИЧНЫЕ Период ИЗ ВТ_ОпорныеТочки),
Номенклатура В (ВЫБРАТЬ РАЗЛИЧНЫЕ Номенклатура ИЗ ВТ_ОпорныеТочки)
) КАК РезервыНаСкладах
;
Примечание: Вместо подзапросов в параметрах виртуальной таблицы можно использовать параметры запроса СКД, предварительно заполнив их массивами дат и номенклатуры на этапе программного формирования отчета.
Шаг 3. Сборка итогового набора данных
На последнем шаге мы собираем итоговую таблицу, соединяя данные из основного регистра (для получения оборотов или остатков по заказам) с временными таблицами остатков из других регистров с помощью ЛЕВОГО СОЕДИНЕНИЯ.
ВЫБРАТЬ
ЗаказыПоставщикамОстатки.Период,
ЗаказыПоставщикамОстатки.Регистратор,
ЗаказыПоставщикамОстатки.Номенклатура,
ЗаказыПоставщикамОстатки.КоличествоОстаток КАК ЗаказаноОстаток,
ЕСТЬNULL(ВТ_ОстаткиНаСкладах.КоличествоНаСкладе, 0) КАК КоличествоНаСкладе,
ЕСТЬNULL(ВТ_РезервыНаСкладах.КоличествоВРезерве, 0) КАК КоличествоВРезерве
ИЗ
РегистрНакопления.ЗаказыПоставщикам.ОстаткиИОбороты(
,
,
Регистратор,
,
Номенклатура = &НужнаяНоменклатура) КАК ЗаказыПоставщикамОстатки
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_ОстаткиНаСкладах
ПО ЗаказыПоставщикамОстатки.Период = ВТ_ОстаткиНаСкладах.Период
И ЗаказыПоставщикамОстатки.Номенклатура = ВТ_ОстаткиНаСкладах.Номенклатура
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_РезервыНаСкладах
ПО ЗаказыПоставщикамОстатки.Период = ВТ_РезервыНаСкладах.Период
И ЗаказыПоставщикамОстатки.Номенклатура = ВТ_РезервыНаСкладах.Номенклатура
Этот запрос вернет одну плоскую таблицу, которую СКД легко обработает и выведет в отчет. Такой подход является самым надежным и производительным для решения подобных задач.
Этот метод подходит, если вам нужно построить отчет в виде "шахматки" или временной ленты событий. Суть в том, чтобы собрать все движения и остатки из разных регистров в одну таблицу с полем, идентифицирующим показатель.
ВЫБРАТЬ
Т.Период,
Т.Номенклатура,
СУММА(Т.Заказано) КАК Заказано,
СУММА(Т.НаСкладе) КАК НаСкладе,
СУММА(Т.ВРезерве) КАК ВРезерве
ИЗ (
ВЫБРАТЬ
Заказы.Период КАК Период,
Заказы.Номенклатура КАК Номенклатура,
Заказы.КоличествоОстаток КАК Заказано,
0 КАК НаСкладе,
0 КАК ВРезерве
ИЗ
РегистрНакопления.ЗаказыПоставщикам.Остатки(,) КАК Заказы
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
Товары.Период,
Товары.Номенклатура,
0,
Товары.КоличествоОстаток,
0
ИЗ
РегистрНакопления.ТоварыНаСкладах.Остатки(,) КАК Товары
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
Резервы.Период,
Резервы.Номенклатура,
0,
0,
Резервы.КоличествоОстаток
ИЗ
РегистрНакопления.ТоварыВРезервеНаСкладах.Остатки(,) КАК Резервы
) КАК Т
СГРУППИРОВАТЬ ПО
Т.Период,
Т.Номенклатура
Этот запрос объединит все данные, после чего их можно будет сгруппировать. Однако он не решает исходную задачу "получить срез на конкретные даты из другого регистра". Он скорее показывает все имеющиеся данные из всех регистров. Для решения исходной задачи его придется значительно усложнять, что делает подход с временными таблицами более предпочтительным.
Итог: для получения срезов данных из нескольких регистров на моменты времени, определяемые другим регистром, наиболее эффективным и управляемым методом является использование единого запроса с временными таблицами. Он дает полный контроль над логикой получения данных, обеспечивает высокую производительность и позволяет избежать "каши", которую иногда создает механизм связей наборов данных СКД.