Как в отчете 1С СКД отобразить последнее значение числового ресурса вместо суммы в группировках?

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

Мы с вами часто сталкиваемся с задачами по построению отчетов в 1С с использованием Системы Компоновки Данных (СКД). Одним из распространенных вопросов является необходимость отображения в строке итогов отчета не суммы числового ресурса, а его последнего значения по определенной группировке. Стандартное поведение СКД для числовых ресурсов — это их суммирование, что не всегда соответствует нашим требованиям. Например, если нам нужно показать остаток на конец периода, максимальное или минимальное значение может не отражать реального положения дел. Давайте вместе разберем, как решить эту нетривиальную задачу, обеспечив вывод именно последнего значения.

Первый подход: Использование функции «Максимум()» в СКД

Когда мы только начинаем решать эту задачу, часто приходит мысль использовать стандартную функцию агрегации `Максимум()` для нашего числового ресурса. Нам кажется, что если данные отсортированы определенным образом, то максимальное значение может совпасть с последним. Рассмотрим этот подход подробнее.

В параметрах ресурса на закладке "Ресурсы" СКД мы можем установить выражение для нашего числового поля, например, так:


Максимум(ПолеЧисло)

Или, чтобы исключить нулевые или неопределенные значения, которые могут исказить результат:


Максимум(ЕСТЬNULL(ПолеЧисло, Неопределено))

Однако, этот подход имеет существенный недостаток, который часто сбивает с толку. Функция `Максимум()` возвращает именно максимальное значение из всех значений в пределах текущей группировки, а не последнее хронологически или по другому порядку. Это означает, что если последнее значение в группе не является максимальным (например, если значения идут 10, 50, 20, а последнее — 20), мы получим неверный результат (50 вместо 20). Даже если данные в отчете выглядят отсортированными, `Максимум()` работает по всем данным группировки, не учитывая порядок их появления. Таким образом, для корректного получения именно последнего значения по группировке нам потребуется более сложный и точный механизм.

Попытки добавить поле с порядковым номером и потом выбрать максимальный порядковый номер в рамках функции `Максимум()` также не приведут к желаемому результату для ресурса, так как `Максимум()` будет применяться к числовому полю, а не к записи, соответствующей максимальному порядковому номеру. Нам нужен способ, который позволит отобрать саму "последнюю" запись, а уже потом взять ее значение.

Отображение последнего значения через дополнительный набор данных в СКД

Для того чтобы гарантированно получить именно последнее значение числового ресурса по определенной группировке, нам потребуется использовать более продвинутые возможности СКД, а именно — создание дополнительного набора данных. Этот метод позволяет нам сначала отобрать "последнюю" запись по каждому измерению в рамках группировки, а затем использовать ее значение в качестве ресурса. Разберем по шагам, как это реализовать, создав ощущение совместной работы над задачей.

Предположим, у нас есть основной набор данных (давайте назовем его `НаборДанных1`) с некими измерениями (например, `Измерение1`, `Измерение2`) и числовым полем (`ПолеЧисло`), для которого нам нужно получить последнее значение. Ключевым моментом здесь является наличие поля, по которому мы можем определить порядок записей и, соответственно, "последнюю" из них. Это может быть `Дата`, `Время`, `ПорядковыйНомер` (если данные уже имеют такой номер, например, номер строки документа), или любой другой уникальный идентификатор записи, по которому можно однозначно установить порядок.

  1. Подготовка основного набора данных:

    Мы должны убедиться, что наш основной набор данных (`НаборДанных1`) содержит поле, которое позволяет однозначно определить порядок записей в пределах каждой будущей группировки отчета. Давайте назовем это поле `ПорядковыйНомер`. Если такого поля нет в источнике данных (например, в регистрах), его можно добавить как вычисляемое поле в запросе к `НаборДанных1` или как вычисляемое поле в самой СКД. Например, это может быть поле с типом "ДатаВремя" (`МоментВремени` для регистров) или сгенерированный уникальный номер строки (например, по ID документа). Важно, чтобы это поле увеличивалось для "более поздних" записей.

  2. Создание дополнительного набора данных:

    Перейдем на закладку "Наборы данных" в схеме компоновки данных. Создадим новый набор данных. Давайте назовем его "ПоследниеЗначенияПоГруппировкам". Тип источника для него выбираем "Запрос". Этот запрос будет использовать наш `НаборДанных1` для поиска последних значений.

  3. Формирование запроса для дополнительного набора данных:

    Запрос для набора данных "ПоследниеЗначенияПоГруппировкам" будет выглядеть следующим образом. Его основная задача — для каждой уникальной комбинации измерений, по которым мы будем группировать отчет, найти запись с максимальным (последним) `ПорядковыйНомер` и извлечь соответствующее `ПолеЧисло`.

    
    ВЫБРАТЬ
        Данные.Измерение1,
        Данные.Измерение2,
        Данные.ПолеЧисло КАК ПолеЧисло
    ИЗ
        НаборДанных1 КАК Данные
    ГДЕ
        Данные.ПорядковыйНомер = (ВЫБРАТЬ МАКСИМУМ(Данные2.ПорядковыйНомер)
                                ИЗ НаборДанных1 КАК Данные2
                                ГДЕ Данные2.Измерение1 = Данные.Измерение1
                                И Данные2.Измерение2 = Данные.Измерение2)
    

    Разберем этот запрос: мы используем подзапрос, который для каждой комбинации `Измерение1` и `Измерение2` (т.е., для каждой группировки) находит максимальное значение `ПорядковыйНомер`. Затем внешний запрос отбирает только те записи из `НаборДанных1`, которые соответствуют этому максимальному порядковому номеру в своей группе. Это гарантирует, что мы получаем именно последнюю запись по каждой группировке, и извлекаем ее `ПолеЧисло`.

  4. Настройка связи наборов данных (при необходимости):

    Если `НаборДанных1` и "ПоследниеЗначенияПоГруппировкам" не объединены напрямую, мы можем связать их на закладке "Связи наборов данных", если это требуется для других полей. Однако, для данной конкретной задачи, когда второй набор данных уже агрегирует нужные значения, этого может и не потребоваться, если мы будем использовать поля напрямую из "ПоследниеЗначенияПоГруппировкам".

  5. Настройка ресурсов в СКД:

    После того как мы создали дополнительный набор данных "ПоследниеЗначенияПоГруппировкам", который содержит только последние значения `ПолеЧисло` по нужным группировкам, перейдем на закладку "Ресурсы". Для нашего числового поля, которое должно отображать последнее значение, установим выражение. Здесь мы воспользуемся тем фактом, что наш дополнительный набор данных уже содержит по одной "последней" записи для каждой группы:

    
    СУММА(ЕСТЬNULL(ПоследниеЗначенияПоГруппировкам.ПолеЧисло, 0))
    

    Мы используем функцию `СУММА()`, но поскольку наш дополнительный набор данных "ПоследниеЗначенияПоГруппировкам" уже содержит только одну запись (последнюю) для каждой комбинации измерений, эта "сумма" фактически будет равна самому этому последнему значению. Функция `ЕСТЬNULL()` используется для корректной обработки возможных пустых значений, чтобы они не повлияли на итоговый результат.

  6. Проверка соответствия группировок:

    Очень важно, чтобы измерения, по которым мы ищем последнее значение в подзапросе (`Данные2.Измерение1 = Данные.Измерение1 И Данные2.Измерение2 = Данные.Измерение2`), точно соответствовали группировкам, которые мы планируем использовать в отчете СКД. Это обеспечит правильный отбор последних значений для каждой строки итогов. Если группировки в отчете меняются, нам, возможно, придется корректировать запрос во втором наборе данных или добавлять дополнительные поля в него.

Этот метод является наиболее надежным и универсальным для решения задачи получения последнего значения числового ресурса в группировках отчета СКД, так как он напрямую манипулирует данными на уровне запроса, выбирая нужную запись до агрегации. Он позволяет нам точно определить, что именно мы считаем "последним", будь то максимальная дата, максимальный порядковый номер или уникальный идентификатор.

Альтернативные подходы и важные нюансы

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

При работе с системами управления базами данных (СУБД), поддерживающими оконные функции (такие как SQL Server, PostgreSQL, а в некоторых случаях и встроенные механизмы 1С через общие модули или специальные запросы), можно использовать функцию `ROW_NUMBER()`. Она позволяет присвоить порядковый номер каждой строке в рамках определенной группы (партиции) на основе заданного порядка. Например:


ВЫБРАТЬ
    Измерение1,
    Измерение2,
    ПолеЧисло,
    ROW_NUMBER() OVER (PARTITION BY Измерение1, Измерение2 ORDER BY ПорядковыйНомер DESC) AS Ряд
ИЗ
    НаборДанных1

После выполнения такого запроса мы можем отобрать только те записи, у которых `Ряд = 1`, что будет соответствовать самой "последней" записи в каждой группе согласно сортировке по `ПорядковыйНомер DESC`. Однако, прямая интеграция таких сложных SQL-конструкций в запросы СКД, особенно без использования внешних источников данных или временных таблиц, может быть затруднительна или невозможна в стандартных запросах 1С. Поэтому предложенный выше метод с дополнительным набором данных является более универсальным и "родным" для среды 1С Предприятие, поскольку он оперирует стандартными механизмами запросов 1С.

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

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

← На главную