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

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

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

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

В чем причина дублирования строк при попытке использовать МАКСИМУМ

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


ВЫБРАТЬ 
    НоменклатураСегмента.Номенклатура КАК Номенклатура,
    МАКСИМУМ(ТМАКСП.Период) КАК Период,
    ТМАКСП.Сегмент КАК Сегмент
ИЗ
    РегистрСведений.НоменклатураСегмента КАК НоменклатураСегмента
        ВНУТРЕННЕЕ СОЕДИНЕНИЕ ТМАКСП КАК ТМАКСП
        ПО НоменклатураСегмента.Сегмент = ТМАКСП.Сегмент
СГРУППИРОВАТЬ ПО
    ТМАКСП.Сегмент,
    НоменклатураСегмента.Номенклатура

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

Пошаговое решение через пакет запросов и временные таблицы

Чтобы получить строго одну запись по номенклатуре с ее самым последним сегментом, разберем по шагам алгоритм эмуляции среза последних:

  1. Определение актуальных дат для сегментов: На первом шаге находим максимальную дату для каждого сегмента и помещаем результат во временную таблицу.
  2. Определение максимальной даты для каждой номенклатуры: Связываем номенклатуру с актуальными периодами сегментов и вычисляем максимальный период уже в разрезе номенклатуры (без группировки по сегменту).
  3. Итоговое соединение: Соединяем исходные данные с таблицей максимальных дат номенклатуры по двум полям — самой номенклатуре и ее максимальному периоду.

Посмотрим на пример рабочего запроса, реализующего данный подход:


ВЫБРАТЬ 
    МАКСИМУМ(ПИК_КоэффСегмДляЗП.Период) КАК Период,
    ПИК_КоэффСегмДляЗП.Сегмент КАК Сегмент
ПОМЕСТИТЬ ТМАКСП
ИЗ
    РегистрСведений.ПИК_КоэффСегмДляЗП КАК ПИК_КоэффСегмДляЗП
СГРУППИРОВАТЬ ПО
    ПИК_КоэффСегмДляЗП.Сегмент
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ 
    НоменклатураСегмента.Номенклатура КАК Номенклатура,
    МАКСИМУМ(ТМАКСП.Период) КАК Период
ПОМЕСТИТЬ НоменклатураМаксимумПериодСегмента
ИЗ
    РегистрСведений.НоменклатураСегмента КАК НоменклатураСегмента
        ВНУТРЕННЕЕ СОЕДИНЕНИЕ ТМАКСП КАК ТМАКСП
        ПО НоменклатураСегмента.Сегмент = ТМАКСП.Сегмент
СГРУППИРОВАТЬ ПО
    НоменклатураСегмента.Номенклатура
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ 
    НоменклатураСегмента.Номенклатура КАК Номенклатура,
    ТМАКСП.Период КАК Период,
    ТМАКСП.Сегмент КАК Сегмент
ИЗ
    РегистрСведений.НоменклатураСегмента КАК НоменклатураСегмента
        ВНУТРЕННЕЕ СОЕДИНЕНИЕ ТМАКСП КАК ТМАКСП
        ПО НоменклатураСегмента.Сегмент = ТМАКСП.Сегмент
        ВНУТРЕННЕЕ СОЕДИНЕНИЕ НоменклатураМаксимумПериодСегмента КАК НоменклатураМаксимумПериодСегмента
        ПО НоменклатураСегмента.Номенклатура = НоменклатураМаксимумПериодСегмента.Номенклатура
            И ТМАКСП.Период = НоменклатураМаксимумПериодСегмента.Период

Обработка коллизий одинаковых дат и оптимизация производительности

Рассмотрим важную деталь: что произойдет, если номенклатура входит в два разных сегмента, и у обоих сегментов совпадает дата максимального периода? В этом случае внутреннее соединение по совпадению периода вернет две строки на одну номенклатуру.

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


ВЫБРАТЬ 
    НоменклатураСегмента.Номенклатура КАК Номенклатура,
    НоменклатураМаксимумПериодСегмента.Период КАК Период,
    МАКСИМУМ(ТМАКСП.Сегмент) КАК Сегмент
ИЗ
    РегистрСведений.НоменклатураСегмента КАК НоменклатураСегмента
        ВНУТРЕННЕЕ СОЕДИНЕНИЕ ТМАКСП КАК ТМАКСП
        ПО НоменклатураСегмента.Сегмент = ТМАКСП.Сегмент
        ВНУТРЕННЕЕ СОЕДИНЕНИЕ НоменклатураМаксимумПериодСегмента КАК НоменклатураМаксимумПериодСегмента
        ПО НоменклатураСегмента.Номенклатура = НоменклатураМаксимумПериодСегмента.Номенклатура
            И ТМАКСП.Период = НоменклатураМаксимумПериодСегмента.Период
СГРУППИРОВАТЬ ПО
    НоменклатураСегмента.Номенклатура,
    НоменклатураМаксимумПериодСегмента.Период

Обратите внимание на производительность: при работе с большими объемами данных во временных таблицах СУБД выполняет полное сканирование (Table Scan). Чтобы исключить замедление работы, обязательно индексируйте временные таблицы с помощью инструкции ИНДЕКСИРОВАТЬ ПО:


ПОМЕСТИТЬ ТМАКСП
ИЗ
    РегистрСведений.ПИК_КоэффСегмДляЗП КАК ПИК_КоэффСегмДляЗП
СГРУППИРОВАТЬ ПО
    ПИК_КоэффСегмДляЗП.Сегмент
ИНДЕКСИРОВАТЬ ПО
    Сегмент,
    Период
;

Индексация по полям связи позволит СУБД использовать эффективные алгоритмы соединения (Index Seek / Nested Loops / Hash Match), что существенно ускорит выполнение запроса в отчетах.

← На главную