При разработке отчетов, обработок сверки и аналитических панелей часто возникает классическая задача: получить дату и документ последнего движения («срез последних») по списку контрагентов в бухгалтерском регистре. Когда база данных содержит сотни тысяч проводок, стандартные или интуитивные подходы (например, обращение к виртуальным таблицам остатков и оборотов или соединение с неиндексированными подзапросами) могут выполняться недопустимо долго — от нескольких минут до нескольких часов.
В этой статье мы подробно разберем внутреннее устройство регистра бухгалтерии, выясним причины медленной работы типичных решений и составим канонический, максимально производительный текст запроса на платформе 1С:Предприятие 8.
Проанализируем распространенные ошибки при попытке выбрать последнее движение контрагента:
ДвиженияССубконто: Данная виртуальная таблица предназначена для разворота субконто в отдельные колонки для каждой строки проводки. При обращении к ней СУБД строит тяжелые динамические соединения основной таблицы движений с таблицами субконто дебета и кредита. На больших периодах и объемах данных это приводит к колоссальным накладным расходам и сканированию всей таблицы.Обороты без жестких фильтров: Если мы берем таблицу оборотов без указания точного периода и конкретного счета, платформе приходится рассчитывать агрегаты по итогам и таблице движений, что также занимает значительное время.ЛЕВОЕ СОЕДИНЕНИЕ (ВЫБРАТЬ МАКСИМУМ(...) ИЗ ...). СУБД не может заранее рассчитать статистику по результату неиндексированного подзапроса и нередко выбирает худший план выполнения (например, вложенные циклы Nested Loops для сотен тысяч строк).Чтобы запрос отрабатывал за доли секунды, обратимся к физическому устройству регистра бухгалтерии в СУБД. В платформе 1С:Предприятие 8 таблица РегистрБухгалтерии.Хозрасчетный.Субконто является реальной (физической) таблицей базы данных. Она содержит следующие ключевые поля:
Период — дата и время совершения проводки.Регистратор — ссылка на документ, сформировавший проводку.НомерСтроки — позиция проводки внутри документа.Вид — ссылка на вид субконто из плана видов характеристик (например, ВидыСубконтоХозрасчетные.Контрагенты).Значение — конкретное значение субконто (ссылка на элемент справочника Контрагенты).Платформа автоматически строит по этой таблице кластерные и вторичные индексы вида (Вид, Значение, Период). Благодаря этому фильтрация по конкретному виду субконто и поиск максимальной даты по индексу выполняются СУБД мгновенно.
Разберем по шагам надежную схему формирования среза последних на языке запросов 1С:
РегистрБухгалтерии.Хозрасчетный.Субконто, устанавливаем отбор по виду субконто, группируем по контрагенту и вычисляем функцию МАКСИМУМ(Период). Результат сохраняем во временную таблицу с индексацией по контрагенту и периоду.МАКСИМУМ(Регистратор) на найденную максимальную дату.Рассмотрим пример готового пакетного запроса, который эффективно справляется с выборкой последних движений даже на базе со 100 000 контрагентов:
ВЫБРАТЬ
Контрагенты.Ссылка КАК Контрагент
ПОМЕСТИТЬ втКонтрагенты
ИЗ
Справочник.Контрагенты КАК Контрагенты
ГДЕ
НЕ Контрагенты.ЭтоГруппа
// При необходимости отбора по массиву:
// И Контрагенты.Ссылка В (&СписокКонтрагентов)
ИНДЕКСИРОВАТЬ ПО
Контрагент
;
////////////////////////////////////////////////////////////////////////////////
// Шаг 1: Находим максимальную дату движения по каждому контрагенту
ВЫБРАТЬ
ТаблицаСубконто.Значение КАК Контрагент,
МАКСИМУМ(ТаблицаСубконто.Период) КАК Период
ПОМЕСТИТЬ втМаксПериоды
ИЗ
втКонтрагенты КАК втКонтрагенты
ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрБухгалтерии.Хозрасчетный.Субконто КАК ТаблицаСубконто
ПО ТаблицаСубконто.Вид = ЗНАЧЕНИЕ(ПланВидовХарактеристик.ВидыСубконтоХозрасчетные.Контрагенты)
И втКонтрагенты.Контрагент = ТаблицаСубконто.Значение
СГРУППИРОВАТЬ ПО
ТаблицаСубконто.Значение
ИНДЕКСИРОВАТЬ ПО
Контрагент,
Период
;
////////////////////////////////////////////////////////////////////////////////
// Шаг 2: Получаем уникальный регистратор на максимальную дату
ВЫБРАТЬ
втМаксПериоды.Контрагент КАК Контрагент,
втМаксПериоды.Период КАК Период,
МАКСИМУМ(ТаблицаСубконто.Регистратор) КАК Регистратор
ПОМЕСТИТЬ втИтоговыеДвижения
ИЗ
втМаксПериоды КАК втМаксПериоды
ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрБухгалтерии.Хозрасчетный.Субконто КАК ТаблицаСубконто
ПО ТаблицаСубконто.Вид = ЗНАЧЕНИЕ(ПланВидовХарактеристик.ВидыСубконтоХозрасчетные.Контрагенты)
И втМаксПериоды.Контрагент = ТаблицаСубконто.Значение
И втМаксПериоды.Период = ТаблицаСубконто.Период
СГРУППИРОВАТЬ ПО
втМаксПериоды.Контрагент,
втМаксПериоды.Период
ИНДЕКСИРОВАТЬ ПО
Контрагент
;
////////////////////////////////////////////////////////////////////////////////
// Шаг 3: Формируем итоговый результат (включая контрагентов без движений)
ВЫБРАТЬ
втКонтрагенты.Контрагент КАК Контрагент,
втИтоговыеДвижения.Период КАК ДатаПоследнегоДвижения,
втИтоговыеДвижения.Регистратор КАК ДокументПоследнегоДвижения
ИЗ
втКонтрагенты КАК втКонтрагенты
ЛЕВОЕ СОЕДИНЕНИЕ втИтоговыеДвижения КАК втИтоговыеДвижения
ПО втКонтрагенты.Контрагент = втИтоговыеДвижения.Контрагент
Для достижения максимальной скорости выполнения обратите внимание на следующие нюансы:
ИНДЕКСИРОВАТЬ ПО для полей, участвующих в связях ПО последующих запросов пакета. Это позволяет оптимизатору СУБД применять быстрые алгоритмы Merge Join или Index Seek.ИЗ. Перенос промежуточных выборок во временные таблицы дает СУБД актуальную статистику распределения ключей.tempdb, разбейте массив контрагентов на порции по 10 000 – 25 000 элементов и выполняйте запрос в цикле с выгрузкой во внешнюю таблицу значений.Применение физической таблицы Субконто в сочетании с индексированными временными таблицами гарантирует предсказуемую и стабильную скорость выполнения среза последних даже в самых нагруженных базах данных 1С.