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

Программист 1С v8.3 (Обычные формы) Управленческий учет Промышленность, строительство и АПК
← На главную

При разработке сложных отчетов или механизмов проведения документов в 1С часто возникает задача: выбрать из табличной части не все записи, а только «последние». Понятие «последний» может интерпретироваться по-разному: это может быть строка с самым большим порядковым номером или строка с максимальной датой события. В данной статье мы подробно разберем, как работают такие пакетные запросы, проанализируем типичные ошибки и рассмотрим современные способы решения этой задачи.

Разбор логики исходного пакетного запроса

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

В первом пакете мы выбираем все данные из табличной части конкретного документа и сохраняем их во временную таблицу ВсеСтроки. Это стандартный прием, который позволяет избежать многократного обращения к реальным таблицам базы данных, что особенно важно при работе с большими объемами информации.


ВЫБРАТЬ
    ХХХ_НарядЗаданиеСкладИсполнителя.Ссылка КАК Ссылка,
    ХХХ_НарядЗаданиеСкладИсполнителя.НомерСтроки КАК НомерСтроки,
    ХХХ_НарядЗаданиеСкладИсполнителя.Номенклатура КАК Номенклатура,
    ХХХ_НарядЗаданиеСкладИсполнителя.ДатаСклада КАК ДатаСклада
ПОМЕСТИТЬ ВсеСтроки
ИЗ
    Документ.ХХХ_НарядЗадание.ОтгруженоИсполнителем КАК ХХХ_НарядЗаданиеСкладИсполнителя
ГДЕ
    ХХХ_НарядЗаданиеСкладИсполнителя.Ссылка В(&ТекущийДокумент);

Проанализируем второй пакет запроса. Именно здесь кроется ответ на вопрос, почему запрос может выбирать не ту строку, которую ожидает разработчик. Рассмотрим код:


ВЫБРАТЬ
    ВсеСтроки.Ссылка КАК Ссылка,
    ВсеСтроки.ДатаСклада КАК ДатаСклада
ПОМЕСТИТЬ ПоследняяДата
ИЗ
    ВсеСтроки КАК ВсеСтроки
    ВНУТРЕННЕЕ СОЕДИНЕНИЕ (ВЫБРАТЬ
        ВсеСтроки.Ссылка КАК Ссылка,
        МАКСИМУМ(ВсеСтроки.НомерСтроки) КАК НомерСтроки
    ИЗ
        ВсеСтроки КАК ВсеСтроки
    СГРУППИРОВАТЬ ПО
        ВсеСтроки.Ссылка) КАК ПоследниеСтроки
    ПО ВсеСтроки.Ссылка = ПоследниеСтроки.Ссылка
        И ВсеСтроки.НомерСтроки = ПоследниеСтроки.НомерСтроки;

В данном фрагменте используется функция МАКСИМУМ(НомерСтроки). Это означает, что система ищет строку с самым большим индексом (ту, которая была добавлена в документ последней по порядку), игнорируя значение в поле ДатаСклада. Если пользователь изменил дату в последней строке на более раннюю, запрос все равно выберет именно эту строку, так как ее НомерСтроки остался максимальным.

Решение 1: Поиск по максимальной дате

Выясним, как изменить логику, если нам требуется найти именно последнюю дату склада. Для этого во вложенном запросе необходимо сгруппировать данные по ссылке на документ и вычислить МАКСИМУМ(ДатаСклада). Однако будьте осторожны: если в одном документе есть две строки с одинаковой максимальной датой, простое соединение по дате вернет обе строки, что может привести к задвоению итоговых показателей (например, количества).

Рассмотрим более надежный вариант, где мы сначала находим максимальную дату, а затем — максимальный номер строки для этой даты:


ВЫБРАТЬ
    ВсеСтроки.Ссылка КАК Ссылка,
    МАКСИМУМ(ВсеСтроки.ДатаСклада) КАК МаксДата
ПОМЕСТИТЬ МаксимальныеДаты
ИЗ
    ВсеСтроки КАК ВсеСтроки
СГРУППИРОВАТЬ ПО
    ВсеСтроки.Ссылка;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ВсеСтроки.Ссылка,
    МАКСИМУМ(ВсеСтроки.НомерСтроки) КАК НомерСтроки
ПОМЕСТИТЬ ПоследниеЗаписиПоДате
ИЗ
    ВсеСтроки КАК ВсеСтроки
    ВНУТРЕННЕЕ СОЕДИНЕНИЕ МаксимальныеДаты КАК МаксимальныеДаты
    ПО ВсеСтроки.Ссылка = МаксимальныеДаты.Ссылка
    И ВсеСтроки.ДатаСклада = МаксимальныеДаты.МаксДата
СГРУППИРОВАТЬ ПО
    ВсеСтроки.Ссылка;

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

Решение 2: Фильтрация по текущей дате

Если задача стоит иначе — выбрать только те строки, которые относятся к «сегодняшнему» числу, — нам не нужно искать максимум. Достаточно наложить условие в секции ГДЕ. Проанализируем ситуацию: если мы добавим условие ДатаСклада = &ТекущаяДата в первый пакет, то во временную таблицу попадут только актуальные записи.


ГДЕ
    ХХХ_НарядЗаданиеСкладИсполнителя.Ссылка В(&ТекущийДокумент)
    И ХХХ_НарядЗаданиеСкладИсполнителя.ДатаСклада = &ТекущаяДата

Важный нюанс: при сравнении дат в запросах 1С всегда учитывается время. Если в реквизите ДатаСклада хранится дата со временем (например, 15.05.2023 14:30), то простое сравнение с началом дня не сработает. В таком случае следует использовать функцию НАЧАЛОПЕРИОДА(ДатаСклада, ДЕНЬ).

Решение 3: Использование оконных функций (Платформа 8.3.20+)

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

Посмотрим на пример, как получить последнюю строку для каждого документа одним коротким запросом:


ВЫБРАТЬ
    *,
    НОМЕР_СТРОКИ() НАД (ПАРТИЦИЯ ПО ВсеСтроки.Ссылка УПОРЯДОЧИТЬ ПО ВсеСтроки.ДатаСклада УБЫВ, ВсеСтроки.НомерСтроки УБЫВ) КАК НомерЗаписи
ПОМЕСТИТЬ ПронумерованныеСтроки
ИЗ
    ВсеСтроки;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ * ИЗ ПронумерованныеСтроки ГДЕ НомерЗаписи = 1;

Здесь ПАРТИЦИЯ ПО разделяет данные по документам, а УПОРЯДОЧИТЬ ПО задает критерий «последности». Строка с самой свежей датой получит НомерЗаписи = 1.

Оптимизация и завершение работы

При работе с пакетными запросами важно помнить о производительности. Проанализируем несколько правил хорошего тона в разработке на 1С:

  1. Индексация: Если во временной таблице ВсеСтроки ожидается большое количество данных, обязательно добавьте индексацию по полю Ссылка. Это ускорит операцию соединения в последующих пакетах.
  2. Уничтожение таблиц: Использование команды УНИЧТОЖИТЬ в конце запроса помогает серверу СУБД быстрее освободить временные ресурсы. Хотя платформа делает это автоматически, в сложных алгоритмах явное удаление считается правильным подходом.
  3. Минимизация вложенных запросов: Старайтесь избегать глубокой вложенности запросов (запрос в запросе в запросе). Пакетная структура с временными таблицами гораздо легче читается и отлаживается через консоль запросов.

Подводя итог, можно сказать, что функционал ПоследняяДата в вашем коде — это всего лишь имя временной таблицы, логика наполнения которой полностью зависит от выбранных функций агрегации (МАКСИМУМ) и полей соединения. Всегда проверяйте, по какому полю ищется этот максимум, чтобы результат соответствовал ожиданиям бизнеса.

← На главную