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