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