Срез последних без использования виртуальной таблицы
Условие
Напишите запрос, который реализует срез последних из регистра сведений, но без использования виртуальной таблицы "СрезПоследних".
Дано:
- Периодический регистр сведений "КурсыВалют" с измерением "Валюта" и ресурсом "Курс"
Нужно получить последний курс каждой валюты на заданную дату.
Подсказка
Используйте подзапрос с группировкой для определения максимальной даты для каждой валюты.
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Решение
Обзор задачи
Это продвинутая задача на работу с периодическими регистрами сведений. СрезПоследних — это встроенная виртуальная таблица в 1С, которая упрощает получение последних значений. Но нужно понимать, как это работает под капотом — через подзапросы, группировку и определение максимальных дат. Это тест на понимание:
- Периодических регистров сведений
- Группировки и функции MAX()
- Корреляции между таблицами
- Оптимизации запросов к историческим данным
Математическая логика
Срез последних реализуется в несколько шагов:
- Для каждого измерения (Валюта) находим максимальную дату (Период)
- Присоединяем исходный регистр обратно по Валюте И Периоду
- Получаем значения (Курс) для этих комбинаций
Решение 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
Выбрать
Курсы.Валюта,
Курсы.Период,
Курсы.Курс
Из
РегистрСведений.КурсыВалют Как Курсы
Внутреннее Соединение
(
Выбрать
КурсыВнут.Валюта,
МАКС(КурсыВнут.Период) Как МаксимальныйПериод
Из
РегистрСведений.КурсыВалют Как КурсыВнут
Где
КурсыВнут.Период <= &ДатаНА
Группировать По
КурсыВнут.Валюта
) Как МаксимальныеДаты
На Курсы.Валюта = МаксимальныеДаты.Валюта
И Курсы.Период = МаксимальныеДаты.МаксимальныйПериод
Ключевые моменты
- GROUP BY + МАКС() — находит максимальный период для каждого значения измерения
- Внутреннее Соединение — привязывает полные данные обратно
- Дата&ДатаНА — ограничивает поиск только соответствующими периодами
- Упорядочить По — для удобного просмотра результатов
- Производительность — зависит от индексов на (Валюта, Период)