Как самостоятельно сделать срез последних?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Срез последних регистра накопления: реализация без встроенной функции
Вопрос о "срезе последних" — классический вопрос на собеседованиях 1С. Проверяет понимание работы регистров накопления и навыки написания оптимальных запросов. Рассмотрю несколько подходов.
Что такое срез последних
Срез последних — это встроенная функция в 1С для получения значений регистра накопления на последнюю дату по каждому измерению:
// Встроенная функция (стандартный способ)
СрезПоследних = РегистрыНакопления.ДвижениеДенежныхСредств.СрезПоследних(
Новый Массив());
// Результат: для каждой комбинации измерений последнее значение
// Счет: "Счет 1" → Сумма: 100000
// Счет: "Счет 2" → Сумма: 50000
Однако, если встроенной функции недостаточно или её нет (например, в старых версиях), нужно реализовать самостоятельно.
Способ 1: Через запрос с максимальной датой
Самый простой и надежный способ:
Процедура ПолучитьСрезПоследних()
// Шаг 1: получаем максимальную дату для каждого измерения
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ
| ДДС.Счет,
| МАКСИМУМ(ДДС.Период) КАК МаксимальнаяДата
|ИЗ
| РегистрНакопления.ДвижениеДенежныхСредств КАК ДДС
|СГРУППИРОВАТЬ ПО
| ДДС.Счет";
ТаблицаМаксДат = Запрос.Выполнить().Выгрузить();
// Шаг 2: для каждой максимальной даты получаем значения
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ
| ДДС.Счет,
| ДДС.Период,
| ДДС.Сумма
|ИЗ
| РегистрНакопления.ДвижениеДенежныхСредств КАК ДДС
| ВНУТРЕННЕЕ СОЕДИНЕНИЕ (ВЫБРАТЬ
| МаксДаты.Счет,
| МаксДаты.МаксимальнаяДата
| ИЗ
| &МаксимальныеДаты КАК МаксДаты) КАК ПоследниеДаты
| ПО ДДС.Счет = ПоследниеДаты.Счет
| И ДДС.Период = ПоследниеДаты.МаксимальнаяДата
|ПОРЯДОК ПО
| ДДС.Счет";
Запрос.УстановитьПараметр("МаксимальныеДаты", ТаблицаМаксДат);
СрезПоследних = Запрос.Выполнить().Выгрузить();
Возврат СрезПоследних;
КонецПроцедуры
Минусы: два запроса, может быть медленно на больших объемах.
Способ 2: Через ROW_NUMBER (для PostgreSQL)
Оптимальный способ — использовать оконные функции:
Процедура ПолучитьСрезПоследнихОптимально()
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ
| ДДС.Счет,
| ДДС.Период,
| ДДС.Сумма
|ИЗ
| (ВЫБРАТЬ
| ДДС2.Счет,
| ДДС2.Период,
| ДДС2.Сумма,
| ROW_NUMBER() ПО (РАЗДЕЛ ПО ДДС2.Счет ПОРЯДОК ПО ДДС2.Период DESC) КАК НомерСтроки
| ИЗ
| РегистрНакопления.ДвижениеДенежныхСредств КАК ДДС2) КАК ДДС
|ГДЕ
| ДДС.НомерСтроки = 1
|ПОРЯДОК ПО
| ДДС.Счет";
СрезПоследних = Запрос.Выполнить().Выгрузить();
Возврат СрезПоследних;
КонецПроцедуры
Преимущества: один запрос, очень быстро даже на больших объемах.
Минусы: требует поддержки оконных функций в СУБД (работает в PostgreSQL, MS SQL Server).
Способ 3: Через LEFT JOIN (универсальный)
Работает везде, оптимален для мелких таблиц:
Процедура ПолучитьСрезПоследнихЛевоеСоединение()
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ
| ДДС1.Счет,
| ДДС1.Период,
| ДДС1.Сумма
|ИЗ
| РегистрНакопления.ДвижениеДенежныхСредств КАК ДДС1
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ДвижениеДенежныхСредств КАК ДДС2
| ПО ДДС1.Счет = ДДС2.Счет
| И ДДС1.Период < ДДС2.Период
|ГДЕ
| ДДС2.Период ЕСТЬ NULL
|ПОРЯДОК ПО
| ДДС1.Счет";
СрезПоследних = Запрос.Выполнить().Выгрузить();
Возврат СрезПоследних;
КонецПроцедуры
Логика: объединяем таблицу с самой собой и оставляем записи, у которых нет более поздних дат. Это будут последние записи.
Минусы: может быть медленно на огромных таблицах из-за декартова произведения.
Способ 4: В коде (для очень специальных случаев)
Если нужна особая логика или нельзя писать сложные запросы:
Процедура ПолучитьСрезПоследнихВКоде()
// Шаг 1: получаем все записи (с фильтром по дате, если нужно)
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ
| ДДС.Счет,
| ДДС.Период,
| ДДС.Сумма
|ИЗ
| РегистрНакопления.ДвижениеДенежныхСредств КАК ДДС
|ПОРЯДОК ПО
| ДДС.Счет,
| ДДС.Период УБЫВ";
ВсеЗаписи = Запрос.Выполнить().Выгрузить();
// Шаг 2: обработка в коде
СрезПоследних = Новая ТаблицаЗначений;
СрезПоследних.Колонки.Добавить("Счет");
СрезПоследних.Колонки.Добавить("Период");
СрезПоследних.Колонки.Добавить("Сумма");
ПредыдущийСчет = Неопределено;
Для Каждого Запись Из ВсеЗаписи Цикл
Если Запись.Счет <> ПредыдущийСчет Тогда
НоваяСтрока = СрезПоследних.Добавить();
НоваяСтрока.Счет = Запись.Счет;
НоваяСтрока.Период = Запись.Период;
НоваяСтрока.Сумма = Запись.Сумма;
ПредыдущийСчет = Запись.Счет;
КонецЕсли;
КонецЦикла;
Возврат СрезПоследних;
КонецПроцедуры
Минусы: медленнее запросов, требует загрузки данных в память.
Сравнение методов
| Способ | Скорость | Простота | Универсальность |
|---|---|---|---|
| Два запроса (MAX + JOIN) | Средняя | Высокая | Хорошая |
| ROW_NUMBER | Высокая | Средняя | PostgreSQL/MSSQL |
| LEFT JOIN (само с собой) | Средняя | Высокая | Отличная |
| В коде | Низкая | Высокая | Отличная |
Рекомендация
Для production кода я рекомендую:
- Первый вариант (MAX + JOIN) — если базовая производительность приемлема
- LEFT JOIN — если нужна универсальность и нет огромных таблиц
- ROW_NUMBER — если известна СУБД (PostgreSQL) и нужна максимальная скорость
- Встроенная функция СрезПоследних() — если версия 1С позволяет
В итоге, срез последних — это классическая задача оптимизации данных, и вариантов решения много. Выбор зависит от контекста, объемов данных и требований к производительности.