← Назад к вопросам

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

2.0 Middle🔥 221 комментариев
#Запросы и оптимизация#Регистры

Условие

Напишите запрос, который реализует срез последних из регистра сведений, но без использования виртуальной таблицы "СрезПоследних".

Дано:

  • Периодический регистр сведений "КурсыВалют" с измерением "Валюта" и ресурсом "Курс"

Нужно получить последний курс каждой валюты на заданную дату.

Подсказка

Используйте подзапрос с группировкой для определения максимальной даты для каждой валюты.

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Решение

Обзор задачи

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

  • Периодических регистров сведений
  • Группировки и функции MAX()
  • Корреляции между таблицами
  • Оптимизации запросов к историческим данным

Математическая логика

Срез последних реализуется в несколько шагов:

  1. Для каждого измерения (Валюта) находим максимальную дату (Период)
  2. Присоединяем исходный регистр обратно по Валюте И Периоду
  3. Получаем значения (Курс) для этих комбинаций

Решение 1: С подзапросом через GROUP BY и MAX

Выбрать
    Курсы.Валюта,
    Курсы.Период,
    Курсы.Курс
Из
    РегистрСведений.КурсыВалют Как Курсы
    Внутреннее Соединение
        (
            Выбрать
                КурсыВнут.Валюта,
                МАКС(КурсыВнут.Период) Как МаксимальныйПериод
            Из
                РегистрСведений.КурсыВалют Как КурсыВнут
            Где
                КурсыВнут.Период <= &ДатаНА
            Группировать По
                КурсыВнут.Валюта
        ) Как МаксимальныеДаты
        На Курсы.Валюта = МаксимальныеДаты.Валюта
        И Курсы.Период = МаксимальныеДаты.МаксимальныйПериод
Упорядочить По
    Курсы.Валюта

Описание: Находим максимальный период для каждой валюты в подзапросе, затем присоединяем основный регистр по обоим полям.

Решение 2: С условием в WHERE через коррелированный подзапрос

Выбрать
    Курсы.Валюта,
    Курсы.Период,
    Курсы.Курс
Из
    РегистрСведений.КурсыВалют Как Курсы
Где
    Курсы.Период = 
        (
            Выбрать
                МАКС(КурсыПоиск.Период)
            Из
                РегистрСведений.КурсыВалют Как КурсыПоиск
            Где
                КурсыПоиск.Валюта = Курсы.Валюта
                И КурсыПоиск.Период <= &ДатаНА
        )
Упорядочить По
    Курсы.Валюта

Описание: Для каждой строки проверяем, является ли её период максимальным для этой валюты.

Решение 3: С явной сортировкой и выбором первого

Выбрать
    Курсы.Валюта,
    Курсы.Период,
    Курсы.Курс
Из
    РегистрСведений.КурсыВалют Как Курсы
    Внутреннее Соединение
        (
            Выбрать Первые По 1
                КурсыСорт.Валюта,
                КурсыСорт.Период,
                КурсыСорт.Курс
            Из
                РегистрСведений.КурсыВалют Как КурсыСорт
            Где
                КурсыСорт.Период <= &ДатаНА
            Упорядочить По
                КурсыСорт.Валюта,
                КурсыСорт.Период Убыв
        ) Как ПервыеПоВалюте
        На Курсы.Валюта = ПервыеПоВалюте.Валюта
        И Курсы.Период = ПервыеПоВалюте.Период
Упорядочить По
    Курсы.Валюта

Описание: Для каждой валюты берём первую (последнюю по дате) запись после сортировки.

Решение 4: Через виртуальную таблицу РАЗЛИЧНЫЕ (DISTINCT)

Выбрать
    Курсы.Валюта,
    Курсы.Период,
    Курсы.Курс
Из
    (
        Выбрать
            КурсыВнут.Валюта,
            КурсыВнут.Период,
            КурсыВнут.Курс,
            РАНГ() Над (Раздел По Валюта Упорядочить По Период Убыв) Как РангПоДате
        Из
            РегистрСведений.КурсыВалют Как КурсыВнут
        Где
            КурсыВнут.Период <= &ДатаНА
    ) Как ПроранжированныеКурсы
Где
    ПроранжированныеКурсы.РангПоДате = 1
Упорядочить По
    ПроранжированныеКурсы.Валюта

Описание: Используем оконную функцию РАНГ для нумерации записей в пределах каждой валюты, затем берём первую (рейтинг = 1).

Решение 5: С LIMIT и GROUP BY в подзапросе

Выбрать
    МаксКурсы.Валюта,
    МаксКурсы.Период,
    МаксКурсы.Курс
Из
    (
        Выбрать
            КурсыВнут.Валюта,
            МАКС(КурсыВнут.Период) Как МаксПериод
        Из
            РегистрСведений.КурсыВалют Как КурсыВнут
        Где
            КурсыВнут.Период <= &ДатаНА
        Группировать По
            КурсыВнут.Валюта
    ) Как МаксПериоды
    Внутреннее Соединение РегистрСведений.КурсыВалют Как МаксКурсы
        На МаксПериоды.Валюта = МаксКурсы.Валюта
        И МаксПериоды.МаксПериод = МаксКурсы.Период
Упорядочить По
    МаксКурсы.Валюта

Решение 6: Альтернативный вариант с МАКСИМАЛЬНОЕ ИЗ

Выбрать
    КурсыВыбор.Валюта,
    КурсыВыбор.Период,
    КурсыВыбор.Курс
Из
    РегистрСведений.КурсыВалют Как КурсыВыбор
Где
    НЕ Существует
        (
            Выбрать 1
            Из
                РегистрСведений.КурсыВалют Как КурсыБолее
            Где
                КурсыБолее.Валюта = КурсыВыбор.Валюта
                И КурсыБолее.Период > КурсыВыбор.Период
                И КурсыБолее.Период <= &ДатаНА
        )
    И КурсыВыбор.Период <= &ДатаНА
Упорядочить По
    КурсыВыбор.Валюта

Описание: Отбираем записи, для которых НЕ существует более свежей записи этой валюты.

Оптимальное решение

Для production кода — Решение 1:

Выбрать
    Курсы.Валюта,
    Курсы.Период,
    Курсы.Курс
Из
    РегистрСведений.КурсыВалют Как Курсы
    Внутреннее Соединение
        (
            Выбрать
                КурсыВнут.Валюта,
                МАКС(КурсыВнут.Период) Как МаксимальныйПериод
            Из
                РегистрСведений.КурсыВалют Как КурсыВнут
            Где
                КурсыВнут.Период <= &ДатаНА
            Группировать По
                КурсыВнут.Валюта
        ) Как МаксимальныеДаты
        На Курсы.Валюта = МаксимальныеДаты.Валюта
        И Курсы.Период = МаксимальныеДаты.МаксимальныйПериод
Упорядочить По
    Курсы.Валюта

Это решение:

  • Понятно — видна логика определения максимальных дат
  • Эффективно — индексы по Валюте помогают GROUP BY
  • Универсально — работает во всех версиях 1С
  • Поддерживаемо — легко изменить критерии отбора

Использование в коде 1С

Процедура ПолучитьПоследниеКурсыВалют(ДатаНА = Неопределено)
    
    Если ДатаНА = Неопределено Тогда
        ДатаНА = Текущая Дата();
    КонецЕсли;
    
    ТекстЗапроса = "Выбрать
        |    Курсы.Валюта,
        |    Курсы.Период,
        |    Курсы.Курс
        |Из
        |    РегистрСведений.КурсыВалют Как Курсы
        |    Внутреннее Соединение
        |        (
        |            Выбрать
        |                КурсыВнут.Валюта,
        |                МАКС(КурсыВнут.Период) Как МаксимальныйПериод
        |            Из
        |                РегистрСведений.КурсыВалют Как КурсыВнут
        |            Где
        |                КурсыВнут.Период <= &ДатаНА
        |            Группировать По
        |                КурсыВнут.Валюта
        |        ) Как МаксимальныеДаты
        |        На Курсы.Валюта = МаксимальныеДаты.Валюта
        |        И Курсы.Период = МаксимальныеДаты.МаксимальныйПериод
        |Упорядочить По
        |    Курсы.Валюта";
    
    Запрос = Новый Запрос(ТекстЗапроса);
    Запрос.УстановитьПараметр("ДатаНА", ДатаНА);
    
    РезультатЗапроса = Запрос.Выполнить();
    ТаблицаРезультата = РезультатЗапроса.Выгрузить();
    
    Для каждого Строка Из ТаблицаРезультата Цикл
        Сообщить("Валюта: " + Строка.Валюта + ", Период: " + Строка.Период + ", Курс: " + Строка.Курс);
    КонецЦикла;
    
КонецПроцедуры

Сравнение подходов

РешениеСложностьЧитаемостьПроизводительностьКогда использовать
1ПростоеОтличнаяОтличнаяProduction, стандартный случай
2СредниеХорошаяСредняяКогда нужна корреляция
3СложноеХорошаяХорошаяТребует ПЕРВЫЕ и УПОРЯДОЧИТЬ
4СложноеХорошаяОтличнаяБольшие объёмы (если версия поддерживает оконные функции)
5ПростоеХорошаяОтличнаяАльтернатива решению 1
6СложноеСредняяСредняяЛогика "без более свежей"

Эквивалентность со СрезПоследних

// Встроенная виртуальная таблица
Выбрать * Из РегистрСведений.КурсыВалют.СрезПоследних(&ДатаНА)

// Эквивалентна нашему Решению 1
Выбрать
    Курсы.Валюта,
    Курсы.Период,
    Курсы.Курс
Из
    РегистрСведений.КурсыВалют Как Курсы
    Внутреннее Соединение
        (
            Выбрать
                КурсыВнут.Валюта,
                МАКС(КурсыВнут.Период) Как МаксимальныйПериод
            Из
                РегистрСведений.КурсыВалют Как КурсыВнут
            Где
                КурсыВнут.Период <= &ДатаНА
            Группировать По
                КурсыВнут.Валюта
        ) Как МаксимальныеДаты
        На Курсы.Валюта = МаксимальныеДаты.Валюта
        И Курсы.Период = МаксимальныеДаты.МаксимальныйПериод

Ключевые моменты

  1. GROUP BY + МАКС() — находит максимальный период для каждого значения измерения
  2. Внутреннее Соединение — привязывает полные данные обратно
  3. Дата&ДатаНА — ограничивает поиск только соответствующими периодами
  4. Упорядочить По — для удобного просмотра результатов
  5. Производительность — зависит от индексов на (Валюта, Период)
Срез последних без использования виртуальной таблицы | PrepBro