При разработке отчетов на платформе 1С программисты часто сталкиваются с задачей, когда в одной таблице необходимо вывести текущие остатки по регистру накопления и одновременно указать дату последнего изменения этого остатка или ссылку на последний документ (регистратор). Стандартная виртуальная таблица Остатки возвращает только измерения и ресурсы, в ней нет полей Период или Регистратор, так как остаток — это агрегированное значение на конкретный момент времени, а не конкретная запись.
В данной статье мы подробно разберем, как правильно «подтянуть» дату последнего движения к остаткам, рассмотрим различные способы реализации этой задачи и проанализируем нюансы производительности каждого метода.
Этот метод считается наиболее оптимальным с точки зрения производительности и корректности данных. Его суть заключается в том, чтобы сначала вычислить дату последнего движения для каждой комбинации измерений, сохранить эти данные во временную таблицу, а затем соединить их с виртуальной таблицей остатков.
Рассмотрим пошагово, как составить такой запрос. Представим, что у нас есть регистр накопления Расш_Бюджет с измерениями Организация и Проект.
Шаг 1. Получение последних дат движений
Сначала обратимся к физической таблице движений регистра. Нам нужно отобрать записи, которые были сделаны не позднее интересующей нас даты, и сгруппировать их по измерениям, вычислив максимальное значение поля Период.
ВЫБРАТЬ
Расш_Бюджет.Организация КАК Организация,
Расш_Бюджет.Проект КАК Проект,
МАКСИМУМ(Расш_Бюджет.Период) КАК ПериодПоследнегоДвижения
ПОМЕСТИТЬ ВТ_ПоследниеДаты
ИЗ
РегистрНакопления.Расш_Бюджет КАК Расш_Бюджет
ГДЕ
Расш_Бюджет.Период <= &ДатаКонца
И Расш_Бюджет.Активность = ИСТИНА
СГРУППИРОВАТЬ ПО
Расш_Бюджет.Организация,
Расш_Бюджет.Проект;
Проанализируем этот фрагмент кода. Обратите внимание на условие Активность = ИСТИНА. Это критически важный момент: если документ был отменен или запись сторнирована, она все равно остается в физической таблице, но перестает влиять на остатки. Без этого фильтра вы рискуете получить дату документа, который фактически не участвует в формировании остатка.
Шаг 2. Соединение остатков с полученными датами
Теперь мы можем получить остатки из виртуальной таблицы и приклеить к ним нашу дату из временной таблицы ВТ_ПоследниеДаты.
ВЫБРАТЬ
Остатки.Организация КАК Организация,
Остатки.Проект КАК Проект,
Остатки.БюджетОстаток КАК БюджетОстаток,
ЕСТЬNULL(ПоследниеДаты.ПериодПоследнегоДвижения, ДАТАВРЕМЯ(1, 1, 1)) КАК ДатаПоследнегоДвижения
ИЗ
РегистрНакопления.Расш_Бюджет.Остатки(&ДатаКонца, ) КАК Остатки
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_ПоследниеДаты КАК ПоследниеДаты
ПО Остатки.Организация = ПоследниеДаты.Организация
И Остатки.Проект = ПоследниеДаты.Проект
Выясним причину использования ЛЕВОЕ СОЕДИНЕНИЕ. Мы используем его для того, чтобы в отчет попали все остатки, даже если по какой-то причине (например, после свертки базы) записи в физической таблице отсутствуют, а итоги в виртуальной таблице есть. Функция ЕСТЬNULL поможет корректно обработать пустые значения дат.
Существует альтернативный способ — использование виртуальной таблицы ОстаткиИОбороты с периодичностью Регистратор. Рассмотрим подробнее этот подход. Система позволяет вызывать таблицу следующим образом:
ВЫБРАТЬ
Бюджет.Организация,
Бюджет.Проект,
Бюджет.Период КАК ДатаДвижения,
Бюджет.Регистратор,
Бюджет.БюджетКонечныйОстаток
ИЗ
РегистрНакопления.Расш_Бюджет.ОстаткиИОбороты(&НачалоПериода, &КонецПериода, Регистратор, , ) КАК Бюджет
Однако разберем по шагам, почему этот метод может быть опасен:
ОстаткиИОбороты с детализацией до регистратора заставляет систему рассчитывать остаток на момент каждого движения. Если у вас тысячи документов за период, запрос будет выполняться крайне медленно.ВЫБРАТЬ ПЕРВЫЕ 1 с сортировкой по периоду убывания), что еще сильнее усложнит запрос.Данный метод допустим только в тех случаях, когда выборка делается по очень узкому фильтру (например, по одной конкретной номенклатуре или одному проекту) и за короткий период.
Рассмотрим ситуацию, когда в одну и ту же секунду (один и тот же Период) было проведено два документа. Если мы ищем просто МАКСИМУМ(Период), мы найдем время, но не сможем однозначно определить, какой документ был «последним» в логической последовательности 1С.
Если вам нужно получить не просто дату, а именно ссылку на Регистратор, рекомендуется использовать следующий алгоритм:
Период для каждого измерения (как в Методе 1).МАКСИМУМ(МоментВремени).Это гарантирует, что даже при совпадении секунд вы выберете тот документ, который стоит последним в последовательности базы данных.
Посмотрим на ситуацию со стороны СУБД. Когда мы делаем СГРУППИРОВАТЬ ПО измерениям физической таблицы, 1С обращается к индексам. Чтобы запрос из Метода 1 летал «молниеносно», убедитесь, что в конфигураторе у измерений Организация и Проект включено свойство «Индексировать» (или «Индексировать с доп. упорядочиванием»).
В регистрах накопления 1С автоматически создает индекс по Периоду и измерениям, но прямой поиск максимума по большой физической таблице без правильных индексов может привести к Full Scan (полному сканированию таблицы), что вызовет блокировки и недовольство пользователей.
Проанализируем еще несколько важных моментов, которые помогут избежать ошибок:
Остатки по умолчанию не возвращает строки, где остаток равен нулю. Если вам нужно видеть дату последнего движения даже для «обнулившихся» позиций, за основу запроса нужно брать физическую таблицу или таблицу Обороты.Остатки(&Дата, Организация = &Организация), а не в секцию ГДЕ. Это позволяет платформе 1С оптимизировать запрос на уровне SQL и не считывать лишние данные.&Дата (начало дня) и Граница (включая или исключая движения самого момента). Обычно для получения актуальных остатков используют Новый Граница(КонецДня(НужнаяДата), ВидГраницы.Включая).Подводя итог, можно сказать, что наиболее надежным и профессиональным способом является использование Метода 1 с предварительным сбором максимальных дат во временную таблицу. Это обеспечивает чистоту кода, высокую скорость работы и легкость отладки запроса в консоли.