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