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

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

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

Разберем эту ситуацию на конкретном примере. Предположим, у нас есть таблица со следующими полями: КоличествоSellOut, _SUFactor, _Штрихкод и _ДатаАктивации. Наша цель — просуммировать _SUFactor таким образом, чтобы для каждой строки с одинаковым штрихкодом использовался фактор из записи с самой поздней датой активации.

Решение 1: Использование пакетного запроса и временных таблиц

Этот метод является классическим и наиболее универсальным для платформы 1С. Он стабильно работает на всех версиях 8.3 и эффективно использует возможности SQL-сервера. Разберем алгоритм по шагам.

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


ВЫБРАТЬ
    Номенклатура._Штрихкод КАК _Штрихкод,
    МАКСИМУМ(Номенклатура._ДатаАктивации) КАК МаксДатаАктивации,
    СУММА(1) КАК КолЗаписей
ПОМЕСТИТЬ ВТ_МаксимальныеДаты
ИЗ
    Справочник.Номенклатура КАК Номенклатура
ГДЕ
    Номенклатура._Штрихкод В (&МассивШтрихкодов)
СГРУППИРОВАТЬ ПО
    Номенклатура._Штрихкод
;

Шаг 2: Получение итоговых данных. Теперь мы соединяем нашу временную таблицу с основной таблицей справочника (или регистра) по двум полям: штрихкоду и дате. Это позволяет нам "вытащить" значение _SUFactor, соответствующее именно этой максимальной дате.


ВЫБРАТЬ
    ВТ._Штрихкод КАК _Штрихкод,
    ВТ.МаксДатаАктивации КАК _ДатаАктивации,
    ВТ.КолЗаписей * Номенклатура._SUFactor КАК СуммаСУфакторов
ИЗ
    Справочник.Номенклатура КАК Номенклатура
    ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_МаксимальныеДаты КАК ВТ
    ПО ВТ._Штрихкод = Номенклатура._Штрихкод
    И ВТ.МаксДатаАктивации = Номенклатура._ДатаАктивации

Проанализируем этот подход: мы сначала сжали данные до уникальных ключей (штрихкод + дата), а затем развернули их обратно, получив нужный коэффициент. Важный нюанс: если на одну и ту же секунду приходится две записи с разными факторами, внутреннее соединение может удвоить количество строк. Чтобы этого избежать, в группировку первой таблицы стоит добавить МАКСИМУМ(Ссылка) и во втором запросе соединяться по ссылке.

Решение 2: Оконные функции (Платформа 8.3.21 и выше)

Если ваша база работает на современной версии платформы (8.3.21+), мы можем значительно упростить запрос, используя оконные функции. Это позволяет избежать создания временных таблиц и делает код более читаемым.

Рассмотрим, как это работает. Мы можем использовать функцию LAST_VALUE или FIRST_VALUE с определенным порядком сортировки внутри окна. Проанализируем пример логики:

  1. Мы определяем "окно" данных с помощью PARTITION BY _Штрихкод.
  2. Внутри окна мы сортируем записи по дате: ORDER BY _ДатаАктивации DESC.
  3. Берем первое значение фактора из этого отсортированного набора.

Такой подход выполняется за один проход по индексам, что существенно экономит ресурсы сервера при работе с сотнями тысяч записей.

Решение 3: Использование коррелированных подзапросов

В некоторых случаях, когда создание временных таблиц нежелательно (например, в очень простых динамических списках), можно использовать подзапрос в списке выбора. Однако помните, что этот метод может быть медленным на больших объемах данных.


ВЫБРАТЬ
    Т1._Штрихкод,
    (ВЫБРАТЬ ПЕРВЫЕ 1 Т2._SUFactor 
     ИЗ Справочник.Номенклатура КАК Т2 
     ГДЕ Т2._Штрихкод = Т1._Штрихкод 
     УПОРЯДОЧИТЬ ПО Т2._ДатаАктивации УБЫВ) КАК АктуальныйФактор
ИЗ
    Справочник.Номенклатура КАК Т1

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

Оптимизация и работа с индексами

Для того чтобы любой из вышеперечисленных запросов летал, необходимо правильно настроить индексы. В нашем случае критически важен составной индекс по полям _Штрихкод + _ДатаАктивации.

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

Подведем итог: выбор метода зависит от версии вашей платформы и объема данных. Для большинства задач пакетный запрос с временной таблицей (где сначала ищется МАКСИМУМ(Дата)) остается "золотым стандартом" разработки на 1С.

← На главную