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