Как в запросе 1С выбрать последнюю партию из регистра накопления?

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

Рассмотрим часто возникающую задачу при доработке нетиповых конфигураций (например, «Трактиръ: Head-Office» на базе 1С:Предприятие 8.3). Предположим, возникла потребность перевыбрать последнюю партию для строк табличной части документа возврата, так как из-за переделок выпусков продукции текущие привязки сбились. Проанализируем ситуацию, выясним причину частых ошибок и разберем по шагам правильные архитектурные решения.

Почему не работает срез последних?

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

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

Решение 1: Выбор первой записи с сортировкой (для одной позиции)

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

Чтобы получить самый свежий документ-партию, отсортируем выборку по реквизиту ДатаПартии или по стандартному полю Период по убыванию (УБЫВ) и возьмем первую строку.

Важное ограничение платформы 1С при работе с датами: Если сортировать исключительно по дате, можно столкнуться с проблемой одинаковых дат. Если в базе зафиксировано несколько партий (выпусков), созданных в одну и ту же секунду, 1С не гарантирует строгий порядок их выдачи. Результат будет зависеть от внутренней сортировки СУБД.

Чтобы точно спозиционироваться на последней записи, к дате необходимо добавлять сортировку по МоментВремени (если выбираются данные документов) или дополнительно сортировать по внутреннему идентификатору — ссылке на сам документ-партию.

Решение 2: Использование МАКСИМУМ(Период) для списка товаров

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

Разберем по шагам правильный алгоритм работы для табличной части:

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

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


ВЫБРАТЬ
    Таблица3.Ресурс,
    Таблица3.Измерение,
    Таблица3.Период
ИЗ (
    ВЫБРАТЬ
        Таблица1.Измерение КАК Измерение,
        МАКСИМУМ(Таблица1.Период) КАК Период
    ИЗ
        РегистрНакопления.ПартииНоменклатуры.Обороты КАК Таблица1
    ГДЕ
        Таблица1.Период <= &Дата
    СГРУППИРОВАТЬ ПО
        Таблица1.Измерение
) КАК Таблица2
ВНУТРЕННЕЕ СОЕДИНЕНИЕ 
    РегистрНакопления.ПартииНоменклатуры КАК Таблица3
ПО 
    Таблица2.Измерение = Таблица3.Измерение 
    И Таблица2.Период = Таблица3.Период

Оптимизация запроса и исправление частых ошибок

Проанализируем типичные ошибки производительности, которые часто допускают при составлении таких запросов.

1. Использование параметров виртуальных таблиц:
Часто разработчики выносят фильтры по измерениям (например, Склад, Фирма, ХарактеристикаНоменклатуры) в секцию ГДЕ. При работе с виртуальными таблицами это является грубой ошибкой оптимизации. Платформа сначала получит из базы данных весь объем записей по всем складам и фирмам, и только потом применит фильтр. Эти условия обязательно нужно перенести внутрь параметров самой виртуальной таблицы.

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

3. Отказ от вложенных запросов в соединениях:
Внутреннее соединение виртуальной таблицы с подзапросом (как в исходных попытках разработчиков) часто приводит к неоптимальным планам выполнения на стороне СУБД. Рекомендуется разбить сложный запрос на пакетный: сначала выбрать нужные данные во Временную Таблицу (ВТ), проиндексировать ее по полям соединения (например, по номенклатуре и партии), а затем уже использовать эту ВТ в соединении.

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

← На главную