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