При разработке функционала на платформе 1С часто возникает задача получить актуальное значение ресурса периодического регистра сведений на определенную дату. Начинающие разработчики нередко сталкиваются с ситуацией, когда запрос возвращает неверные данные или пустой результат, если на выбранную дату точной записи в регистре нет, хотя более ранние записи существуют.
Разберем типичные ошибки при построении запросов к срезам регистров сведений, выясним причину некорректного поведения и рассмотрим оптимальные способы решения этой задачи с точки зрения стандартов разработки и производительности СУБД.
Посмотрим на исходный пример запроса, который вызывает ошибку:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| МинСуммаСамовывозаСрезПоследних.ГруппаФинКонтр КАК Группа,
| МинСуммаСамовывозаСрезПоследних.МинимальнаяСуммаСамовывоза КАК Миним
|ИЗ
| РегистрСведений.МинСуммаСамовывоза.СрезПоследних КАК МинСуммаСамовывозаСрезПоследних
|ГДЕ
| МинСуммаСамовывозаСрезПоследних.Период = &ДатаПр
| И МинСуммаСамовывозаСрезПоследних.ГруппаФинКонтр.Ссылка = &Тек";
Запрос.УстановитьПараметр("ДатаПр", ЭтаФорма.ДатаПросмотра);
Запрос.УстановитьПараметр("Тек", ЭтотОбъект.ЭтотОбъект.Объект.Ссылка);
Проанализируем основные проблемы приведенного кода:
ГДЕ для фильтрации по периоду. Виртуальная таблица СрезПоследних без переданной в круглых скобках даты рассчитывает актуальные данные на текущий момент времени. Когда в блоке ГДЕ добавляется условие Период = &ДатаПр, система пытается найти запись, дата которой в точности равна переданной. Если последняя запись была внесена раньше указанной даты, запрос вернет пустой результат.ГДЕ заставляет СУБД сначала рассчитать срез последних по абсолютно всем записям таблицы, а затем профильтровать результат. Это существенно замедляет выполнение запроса.ГруппаФинКонтр.Ссылка приводит к формированию неявного соединения (LEFT JOIN) с таблицей справочника, хотя измерение ГруппаФинКонтр само по себе уже содержит прямую ссылку на элемент.Запрос.Выполнить().Выгрузить() создает избыточную нагрузку на память сервера, если требуется получить лишь одно значение.Рассмотрим, как правильно составить запрос с использованием параметров виртуальной таблицы СрезПоследних. Дата и фильтры по измерениям должны передаваться непосредственно в круглых скобках виртуальной таблицы.
Посмотрим на исправленный и оптимизированный пример:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ЕСТЬNULL(МинСуммаСамовывозаСрезПоследних.МинимальнаяСуммаСамовывоза, 0) КАК МинимальнаяСумма
|ИЗ
| РегистрСведений.МинСуммаСамовывоза.СрезПоследних(&Период, ГруппаФинКонтр = &Группа) КАК МинСуммаСамовывозаСрезПоследних";
// Если требуется учесть записи за весь выбранный день, передаем конец суток
Запрос.УстановитьПараметр("Период", КонецДня(ДатаПросмотра));
Запрос.УстановитьПараметр("Группа", Объект.Ссылка);
Выборка = Запрос.Выполнить().Выбрать();
Если Выборка.Следующий() Тогда
МинимальнаяСуммаСамовывоза = Выборка.МинимальнаяСумма;
Иначе
МинимальнаяСуммаСамовывоза = 0;
КонецЕсли;
Разберем ключевые особенности этого решения:
СрезПоследних(&Период, ...) берет самую свежую запись, дата которой меньше или равна значению &Период.КонецДня() позволяет гарантированно включить записи, добавленные в течение дня (например, в 15:30:00), если пользователь выбирает дату без указания времени.ГруппаФинКонтр = &Группа ограничивает объем обрабатываемых данных на уровне СУБД еще до формирования среза.ЕСТЬNULL и метод Выбрать(): предотвращают получение неопределенных значений и экономят оперативную память сервера.Если нам необходимо получить данные только по одному объекту или конкретному набору измерений без построения сложных выборок, писать запрос не обязательно. В платформе 1С предусмотрен встроенный метод менеджера регистра сведений ПолучитьПоследнее().
Рассмотрим пример реализации:
Отбор = Новый Структура("ГруппаФинКонтр", Объект.Ссылка);
ЗначенияРесурсов = РегистрыСведений.МинСуммаСамовывоза.ПолучитьПоследнее(КонецДня(ДатаПросмотра), Отбор);
Если ЗначенияРесурсов.Свойство("МинимальнаяСуммаСамовывоза") Тогда
МинимальнаяСуммаСамовывоза = ЗначенияРесурсов.МинимальнаяСуммаСамовывоза;
Иначе
МинимальнаяСуммаСамовывоза = 0;
КонецЕсли;
Этот метод возвращает структуру, содержащую актуальные значения всех ресурсов регистра на переданный момент времени. Это делает программный код компактным, легко читаемым и не уступающим по производительности простому запросу.
Разберем детали, на которые следует обратить внимание при реализации логики в модуле формы:
Объект.Ссылка будет содержать пустую ссылку. В этом случае срез регистра сведений вернет пустой результат, так как записей с пустой ссылкой в регистре нет. Перед выполнением запроса или метода ПолучитьПоследнее() следует проверять условие Объект.Ссылка.Пустая().&НаСервере или &НаСервереБезКонтекста).Граница с видом ВидГраницы.Включая.