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

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

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

В этой статье мы подробно разберем, как реализовать такой отбор с помощью языка запросов 1С, проанализируем возможные подводные камни, такие как появление дублей, и рассмотрим методы оптимизации производительности.

Метод 1: Использование временной таблицы и внутреннего соединения

Рассмотрим базовый подход, предложенный участниками обсуждения. Если нам нужно найти строку с максимальным значением поля Реквизит1 для каждого Измерение1, мы не можем просто применить агрегатную функцию МАКСИМУМ ко всем полям в одном запросе. Это связано с тем, что нам нужно получить не просто максимальное число, а все связанные с ним данные из этой же строки (например, Реквизит2).

Для решения задачи мы разделим алгоритм на два этапа. Сначала вычислим максимальные значения, а затем «подтянем» остальные данные. Разберем этот процесс по шагам:

  1. Создадим временную таблицу, в которой сгруппируем записи по измерениям и вычислим максимум по целевому реквизиту.
  2. Соединим эту временную таблицу с основной таблицей регистра по всем измерениям и по значению реквизита.

Посмотрим на пример кода запроса:


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

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

В данном примере мы использовали ВНУТРЕННЕЕ СОЕДИНЕНИЕ, которое гарантирует, что в итоговую выборку попадут только те записи, значения которых совпали с вычисленным максимумом.

Метод 2: Решение проблемы дублирующихся максимумов

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

Если бизнес-логика требует получения строго одной записи (например, самой последней по времени среди тех, что имеют максимальный реквизит), нам необходимо усложнить схему до трех шагов. Рассмотрим этот продвинутый вариант:

  1. ВТ1: Находим МАКСИМУМ(Реквизит1) в разрезе измерений.
  2. ВТ2: Находим МАКСИМУМ(Период), соединяя основную таблицу с ВТ1 по измерениям и значению реквизита.
  3. Итоговый запрос: Выбираем данные из физической таблицы регистра, соединяясь с ВТ2 по измерениям и по точному значению периода.

Такой подход исключает дублирование строк и гарантирует однозначность результата.

Метод 3: Использование коррелированных подзапросов в условии ГДЕ

Иногда, если данных в регистре немного, можно использовать более компактную запись без явного создания временных таблиц. В 1С это реализуется через конструкцию В (ВЫБРАТЬ...) в секции ГДЕ. Проанализируем этот пример:


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

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

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

Выясним причину, по которой подобные запросы могут «тормозить». В 1С измерения регистров сведений индексируются автоматически, а вот обычные реквизиты — нет. Когда мы выполняем группировку и поиск максимума по полю Реквизит1, системе приходится сканировать всю таблицу (Full Table Scan).

Чтобы ускорить выполнение запроса, выполните следующие рекомендации:

Пример индексирования временной таблицы:


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

ИНДЕКСИРОВАТЬ ПО
    Измерение1,
    МаксимальныйРеквизит
;

Альтернативный подход для одиночного отбора

Если ваша задача — найти одну единственную запись с максимальным реквизитом для конкретного значения измерения (например, только для одного контрагента), использование группировок будет избыточным. Гораздо эффективнее использовать конструкцию ВЫБРАТЬ ПЕРВЫЕ 1 с упорядочиванием.

Посмотрим на пример:


ВЫБРАТЬ ПЕРВЫЕ 1
    Регистр.Реквизит1,
    Регистр.Реквизит2
ИЗ
    РегистрСведений.НазваниеРегистра КАК Регистр
ГДЕ
    Регистр.Измерение1 = &ПараметрИзмерения
УПОРЯДОЧИТЬ ПО
    Регистр.Реквизит1 УБЫВ,
    Регистр.Период УБЫВ

В данном случае СУБД, используя индекс, мгновенно найдет самую «большую» запись и прекратит поиск, что значительно быстрее, чем агрегация всей таблицы.

Подводя итог, можно сказать, что выбор метода зависит от объема данных и необходимости получения множественных записей. Для массового отбора по всем измерениям используйте Временные Таблицы с обязательным индексированием, а для точечного поиска — Упорядочивание и ВЫБРАТЬ ПЕРВЫЕ 1.

← На главную