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

Как самостоятельно сделать срез последних?

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

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

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

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

Срез последних регистра накопления: реализация без встроенной функции

Вопрос о "срезе последних" — классический вопрос на собеседованиях 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 кода я рекомендую:

  1. Первый вариант (MAX + JOIN) — если базовая производительность приемлема
  2. LEFT JOIN — если нужна универсальность и нет огромных таблиц
  3. ROW_NUMBER — если известна СУБД (PostgreSQL) и нужна максимальная скорость
  4. Встроенная функция СрезПоследних() — если версия 1С позволяет

В итоге, срез последних — это классическая задача оптимизации данных, и вариантов решения много. Выбор зависит от контекста, объемов данных и требований к производительности.

Как самостоятельно сделать срез последних? | PrepBro