При работе с платформой 1С:Предприятие 8 часто возникает необходимость получить информацию о последних операциях или документах для определённых объектов учета. Одна из таких задач – найти документ с максимальным МоментВремени для поля составного типа, например, поля Регистратор регистра бухгалтерии. Эта задача имеет свои особенности, поскольку МоментВремени – это не просто дата, а составной объект, а само поле Регистратор может ссылаться на документы различных типов.
Давайте подробно разберем, как эффективно решить эту проблему, используя методы оптимизации запросов и временные таблицы, чтобы обеспечить корректность и высокую производительность.
Прежде чем перейти к решению, нам необходимо четко понимать, с какими типами данных мы работаем:
Регистратор: Это поле в регистрах (например, в РегистрБухгалтерии.Хозрасчетный) является полем составного типа. Оно может содержать ссылки на документы разных видов (Документ.ПоступлениеТоваровУслуг, Документ.РеализацияТоваровУслуг и т.д.). Прямое "разыменование через точку" (например, Регистратор.Дата) без явного указания типа документа является неоптимальным, так как платформа вынуждена выполнять неявные соединения со всеми возможными таблицами документов, что может сильно замедлить выполнение запроса.МоментВремени: В 1С МоментВремени — это специальный тип данных, который используется для однозначного упорядочивания записей во времени. Он состоит из двух частей:
Такая комбинация позволяет гарантировать уникальный порядок документов, даже если несколько документов были проведены в одну и ту же секунду. При обычном преобразовании в поле Период в виртуальных таблицах регистров (например, РегистрБухгалтерии.Хозрасчетный.Обороты) это поле обычно представляет собой просто дату с точностью до секунды, без ссылки на документ. Однако для полного МоментВремени нам нужна и дата, и ссылка.
Именно из-за составной природы Регистратор и особенностей МоментВремени, прямой запрос типа МАКСИМУМ(ХозрасчетныйОбороты.Регистратор.МоментВремени) не работает так, как мы ожидаем, или вовсе не будет работать корректно.
Как мы уже выяснили, прямое разыменование поля составного типа, такого как Регистратор, является неэффективным. Платформа 1С для такого запроса будет пытаться соединиться со всеми возможными таблицами, на которые может ссылаться поле Регистратор. Если список возможных типов документов большой, это приведет к крайне медленному выполнению запроса.
Для решения этой проблемы и значительного ускорения запросов мы используем оператор ВЫРАЗИТЬ(<Поле> КАК <Тип>). Этот оператор явно указывает системе, к какому конкретному типу данных мы хотим привести составное поле. Таким образом, платформа понимает, что ей нужно выполнить соединение только с одной конкретной таблицей, что существенно сокращает объем работы.
Рассмотрим пример использования ВЫРАЗИТЬ для поля Регистратор, как это было предложено автором темы:
ВЫБОР
КОГДА ХозрасчетныйОбороты.Регистратор ССЫЛКА Документ.ПоступлениеТоваровУслуг
ТОГДА ВЫРАЗИТЬ(ХозрасчетныйОбороты.Регистратор КАК Документ.ПоступлениеТоваровУслуг).Ссылка
КОГДА ХозрасчетныйОбороты.Регистратор ССЫЛКА Документ.РеализацияТоваровУслуг
ТОГДА ВЫРАЗИТЬ(ХозрасчетныйОбороты.Регистратор КАК Документ.РеализацияТоваровУслуг).Ссылка
// ... и так далее для всех необходимых типов документов
КОНЕЦ КАК Регистратор
Здесь мы явно проверяем тип ссылки с помощью ССЫЛКА, а затем, если тип совпадает, приводим поле Регистратор к нужному типу документа и получаем его Ссылка. Это делает запрос значительно более эффективным, поскольку платформа заранее знает, с какой таблицей документов ей работать в каждом случае.
Для получения максимального МоментВремени (то есть, максимально возможной даты и соответствующей ссылки на документ) для поля Регистратор, мы будем использовать трехэтапный подход с временными таблицами. Этот подход позволяет последовательно решить проблему: сначала получить все необходимые данные, затем найти максимальную дату, и, наконец, найти соответствующий этой максимальной дате регистратор.
На первом шаге мы извлекаем из регистра бухгалтерии Хозрасчетный необходимые нам данные: контрагента, договор, период (который в контексте виртуальной таблицы Обороты уже является датой из МоментВремени) и, самое главное, явно выраженную ссылку на регистратор. Для оптимизации мы сразу используем оператор ВЫРАЗИТЬ.
Мы работаем с виртуальной таблицей Обороты регистра бухгалтерии, так как она предоставляет удобный способ получения данных с фильтрацией по регистратору и счетам.
Важно: В этом шаге мы получаем Период, который является датой (обычно до секунды). Если на одну и ту же секунду приходится несколько документов, то поле Регистратор в МоментВремени позволит их упорядочить. Для корректной работы с МАКСИМУМ(Регистратор) нам нужно, чтобы Регистратор был полноценной ссылкой.
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ВЫРАЗИТЬ(ХозрасчетныйОбороты.Субконто1 КАК Справочник.Контрагенты) КАК Контрагент,
ВЫРАЗИТЬ(ХозрасчетныйОбороты.Субконто2 КАК Справочник.ДоговорыКонтрагентов) КАК Договор,
ВЫБОР
КОГДА ХозрасчетныйОбороты.Регистратор ССЫЛКА Документ.ПоступлениеТоваровУслуг
ТОГДА ВЫРАЗИТЬ(ХозрасчетныйОбороты.Регистратор КАК Документ.ПоступлениеТоваровУслуг).Ссылка
КОГДА ХозрасчетныйОбороты.Регистратор ССЫЛКА Документ.РеализацияТоваровУслуг
ТОГДА ВЫРАЗИТЬ(ХозрасчетныйОбороты.Регистратор КАК Документ.РеализацияТоваровУслуг).Ссылка
КОГДА ХозрасчетныйОбороты.Регистратор ССЫЛКА Документ.ПоступлениеНаРасчетныйСчет
ТОГДА ВЫРАЗИТЬ(ХозрасчетныйОбороты.Регистратор КАК Документ.ПоступлениеНаРасчетныйСчет).Ссылка
КОГДА ХозрасчетныйОбороты.Регистратор ССЫЛКА Документ.СписаниеСРасчетногоСчета
ТОГДА ВЫРАЗИТЬ(ХозрасчетныйОбороты.Регистратор КАК Документ.СписаниеСРасчетногоСчета).Ссылка
КОГДА ХозрасчетныйОбороты.Регистратор ССЫЛКА Документ.КорректировкаРеализации
ТОГДА ВЫРАЗИТЬ(ХозрасчетныйОбороты.Регистратор КАК Документ.КорректировкаРеализации).Ссылка
КОГДА ХозрасчетныйОбороты.Регистратор ССЫЛКА Документ.КорректировкаПоступления
ТОГДА ВЫРАЗИТЬ(ХозрасчетныйОбороты.Регистратор КАК Документ.КорректировкаПоступления).Ссылка
КОГДА ХозрасчетныйОбороты.Регистратор ССЫЛКА Документ.КорректировкаДолга
ТОГДА ВЫРАЗИТЬ(ХозрасчетныйОбороты.Регистратор КАК Документ.КорректировкаДолга).Ссылка
КОГДА ХозрасчетныйОбороты.Регистратор ССЫЛКА Документ.ГТДИмпорт
ТОГДА ВЫРАЗИТЬ(ХозрасчетныйОбороты.Регистратор КАК Документ.ГТДИмпорт).Ссылка
КОГДА ХозрасчетныйОбороты.Регистратор ССЫЛКА Документ.ОтчетКомиссионераОПродажах
ТОГДА ВЫРАЗИТЬ(ХозрасчетныйОбороты.Регистратор КАК Документ.ОтчетКомиссионераОПродажах).Ссылка
КОГДА ХозрасчетныйОбороты.Регистратор ССЫЛКА Документ.ОтчетКомитентуОПродажах
ТОГДА ВЫРАЗИТЬ(ХозрасчетныйОбороты.Регистратор КАК Документ.ОтчетКомитентуОПродажах).Ссылка
КОНЕЦ КАК Регистратор,
ХозрасчетныйОбороты.Период КАК Период
ПОМЕСТИТЬ ВТ_Периоды__Срегистратором
ИЗ
РегистрБухгалтерии.Хозрасчетный.Обороты(, &Период, Регистратор, ХозрасчетныйОбороты.Счет В ИЕРАРХИИ (&СписокСчетов) , , , , ) КАК ХозрасчетныйОбороты
ИНДЕКСИРОВАТЬ ПО
Контрагент,
Договор
;
Обратите внимание на использование ИНДЕКСИРОВАТЬ ПО Контрагент, Договор. Это очень важный шаг для оптимизации, так как следующие запросы будут группировать и соединять данные по этим полям. Создание индекса на временной таблице значительно ускорит эти операции.
После того как мы собрали все необходимые данные в первой временной таблице, следующим шагом является определение максимальной даты (Период) для каждой уникальной комбинации Контрагент и Договор. Это позволит нам найти самый "поздний" момент времени для каждой связки контрагента с договором.
Для этого мы используем агрегатную функцию МАКСИМУМ() и группировку по нашим измерениям:
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ВТ_Периоды__Срегистратором.Контрагент КАК Контрагент,
ВТ_Периоды__Срегистратором.Договор КАК Договор,
МАКСИМУМ(ВТ_Периоды__Срегистратором.Период) КАК ДатаПоследнейОперации
ПОМЕСТИТЬ ВТ_Периоды
ИЗ
ВТ_Периоды__Срегистратором КАК ВТ_Периоды__Срегистратором
СГРУППИРОВАТЬ ПО
ВТ_Периоды__Срегистратором.Контрагент,
ВТ_Периоды__Срегистратором.Договор
;
Результатом этого запроса будет таблица, содержащая для каждого контрагента и договора их самую последнюю дату операции. Эта временная таблица ВТ_Периоды будет использоваться на следующем шаге для фильтрации.
На заключительном этапе мы соединяем первую временную таблицу (ВТ_Периоды__Срегистратором), содержащую все детали операций и ссылки на регистраторы, со второй временной таблицей (ВТ_Периоды), где мы определили максимальные даты для каждой группы. Условие соединения будет включать Контрагент, Договор и главное – совпадение Период из первой таблицы с ДатаПоследнейОперации из второй.
В случае, если для одной и той же максимальной даты (секунды) существует несколько регистраторов (что крайне редко, но теоретически возможно для разных документов), нам нужно выбрать один "максимальный" регистратор. Это достигается повторным использованием функции МАКСИМУМ() для поля Регистратор. В контексте ссылок 1С, МАКСИМУМ() будет выбирать ссылку с наибольшим внутренним представлением (фактически, с наибольшим уникальным идентификатором), что соответствует логике "самого позднего" документа, если дата проведения совпадает.
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
МАКСИМУМ(ВТ_Периоды__Срегистратором.Регистратор) КАК Регистратор,
ВТ_Периоды.ДатаПоследнейОперации КАК ДатаПоследнейОперации,
ВТ_Периоды__Срегистратором.Контрагент КАК Контрагент,
ВТ_Периоды__Срегистратором.Договор КАК Договор
ИЗ
ВТ_Периоды__Срегистратором КАК ВТ_Периоды__Срегистратором
ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_Периоды КАК ВТ_Периоды
ПО ВТ_Периоды__Срегистратором.Контрагент = ВТ_Периоды.Контрагент
И ВТ_Периоды__Срегистратором.Договор = ВТ_Периоды.Договор
И ВТ_Периоды__Срегистратором.Период = ВТ_Периоды.ДатаПоследнейОперации
СГРУППИРОВАТЬ ПО
ВТ_Периоды__Срегистратором.Контрагент,
ВТ_Периоды__Срегистратором.Договор,
ВТ_Периоды.ДатаПоследнейОперации
Этот финальный запрос вернет нам для каждой комбинации контрагента и договора максимальную дату операции и соответствующий ей (максимальный по ссылке) документ-регистратор.
ИНДЕКСИРОВАТЬ ПО для временных таблиц является критически важным для производительности, особенно когда эти таблицы используются в последующих соединениях или группировках. Всегда анализируйте, по каким полям будут происходить соединения и группировки, и создавайте по ним индексы.ВЫРАЗИТЬ: Оператор ВЫРАЗИТЬ – мощный инструмент, но его применение должно быть обдуманным. Используйте его только тогда, когда это действительно необходимо для приведения составного типа к конкретному. Чрезмерное или некорректное использование может, наоборот, привести к ошибкам или ухудшению читаемости запроса.&Период и &СписокСчетов: В приведенном примере используются параметры запроса &Период и &СписокСчетов. Это позволяет динамически передавать значения в запрос, что является стандартной практикой и способствует гибкости отчетов и обработок.Контрагент, Договор) и ресурс (ссылка на последний документ-регистратор, а также его дату). Тогда нужные данные можно будет получать максимально быстро через виртуальную таблицу "Срез последних", жертвуя небольшим увеличением объема хранимых данных для повышения производительности запросов. Однако это требует изменения структуры конфигурации.Используя описанный подход с временными таблицами и оператором ВЫРАЗИТЬ, мы можем эффективно и корректно получить максимальный МоментВремени (включая ссылку на регистратор) для поля составного типа, решая распространенную и сложную задачу в программировании на 1С.