Как правильно получить значение периодического регистра сведений на указанную дату?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При разработке функционала на платформе 1С часто возникает задача получить актуальное значение ресурса периодического регистра сведений на определенную дату. Начинающие разработчики нередко сталкиваются с ситуацией, когда запрос возвращает неверные данные или пустой результат, если на выбранную дату точной записи в регистре нет, хотя более ранние записи существуют.

Разберем типичные ошибки при построении запросов к срезам регистров сведений, выясним причину некорректного поведения и рассмотрим оптимальные способы решения этой задачи с точки зрения стандартов разработки и производительности СУБД.

Анализ типичной ошибки в тексте запроса

Посмотрим на исходный пример запроса, который вызывает ошибку:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |    МинСуммаСамовывозаСрезПоследних.ГруппаФинКонтр КАК Группа,
    |    МинСуммаСамовывозаСрезПоследних.МинимальнаяСуммаСамовывоза КАК Миним
    |ИЗ
    |    РегистрСведений.МинСуммаСамовывоза.СрезПоследних КАК МинСуммаСамовывозаСрезПоследних
    |ГДЕ
    |    МинСуммаСамовывозаСрезПоследних.Период = &ДатаПр
    |    И МинСуммаСамовывозаСрезПоследних.ГруппаФинКонтр.Ссылка = &Тек";
Запрос.УстановитьПараметр("ДатаПр", ЭтаФорма.ДатаПросмотра);
Запрос.УстановитьПараметр("Тек", ЭтотОбъект.ЭтотОбъект.Объект.Ссылка);

Проанализируем основные проблемы приведенного кода:

  1. Использование секции ГДЕ для фильтрации по периоду. Виртуальная таблица СрезПоследних без переданной в круглых скобках даты рассчитывает актуальные данные на текущий момент времени. Когда в блоке ГДЕ добавляется условие Период = &ДатаПр, система пытается найти запись, дата которой в точности равна переданной. Если последняя запись была внесена раньше указанной даты, запрос вернет пустой результат.
  2. Отсутствие отбора в параметрах виртуальной таблицы. Наложение условий на измерения регистра в блоке ГДЕ заставляет СУБД сначала рассчитать срез последних по абсолютно всем записям таблицы, а затем профильтровать результат. Это существенно замедляет выполнение запроса.
  3. Неявное разыменование ссылок. Конструкция ГруппаФинКонтр.Ссылка приводит к формированию неявного соединения (LEFT JOIN) с таблицей справочника, хотя измерение ГруппаФинКонтр само по себе уже содержит прямую ссылку на элемент.
  4. Неэффективная работа с выборкой данных. Выгрузка в таблицу значений через Запрос.Выполнить().Выгрузить() создает избыточную нагрузку на память сервера, если требуется получить лишь одно значение.

Способ 1. Исправление и оптимизация текста запроса

Рассмотрим, как правильно составить запрос с использованием параметров виртуальной таблицы СрезПоследних. Дата и фильтры по измерениям должны передаваться непосредственно в круглых скобках виртуальной таблицы.

Посмотрим на исправленный и оптимизированный пример:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |    ЕСТЬNULL(МинСуммаСамовывозаСрезПоследних.МинимальнаяСуммаСамовывоза, 0) КАК МинимальнаяСумма
    |ИЗ
    |    РегистрСведений.МинСуммаСамовывоза.СрезПоследних(&Период, ГруппаФинКонтр = &Группа) КАК МинСуммаСамовывозаСрезПоследних";

// Если требуется учесть записи за весь выбранный день, передаем конец суток
Запрос.УстановитьПараметр("Период", КонецДня(ДатаПросмотра));
Запрос.УстановитьПараметр("Группа", Объект.Ссылка);

Выборка = Запрос.Выполнить().Выбрать();
Если Выборка.Следующий() Тогда
    МинимальнаяСуммаСамовывоза = Выборка.МинимальнаяСумма;
Иначе
    МинимальнаяСуммаСамовывоза = 0;
КонецЕсли;

Разберем ключевые особенности этого решения:

  1. Параметр периода: СрезПоследних(&Период, ...) берет самую свежую запись, дата которой меньше или равна значению &Период.
  2. Учет времени суток: функция КонецДня() позволяет гарантированно включить записи, добавленные в течение дня (например, в 15:30:00), если пользователь выбирает дату без указания времени.
  3. Отбор по измерению: условие ГруппаФинКонтр = &Группа ограничивает объем обрабатываемых данных на уровне СУБД еще до формирования среза.
  4. Функция ЕСТЬNULL и метод Выбрать(): предотвращают получение неопределенных значений и экономят оперативную память сервера.

Способ 2. Использование метода менеджера регистра сведений

Если нам необходимо получить данные только по одному объекту или конкретному набору измерений без построения сложных выборок, писать запрос не обязательно. В платформе 1С предусмотрен встроенный метод менеджера регистра сведений ПолучитьПоследнее().

Рассмотрим пример реализации:


Отбор = Новый Структура("ГруппаФинКонтр", Объект.Ссылка);
ЗначенияРесурсов = РегистрыСведений.МинСуммаСамовывоза.ПолучитьПоследнее(КонецДня(ДатаПросмотра), Отбор);

Если ЗначенияРесурсов.Свойство("МинимальнаяСуммаСамовывоза") Тогда
    МинимальнаяСуммаСамовывоза = ЗначенияРесурсов.МинимальнаяСуммаСамовывоза;
Иначе
    МинимальнаяСуммаСамовывоза = 0;
КонецЕсли;

Этот метод возвращает структуру, содержащую актуальные значения всех ресурсов регистра на переданный момент времени. Это делает программный код компактным, легко читаемым и не уступающим по производительности простому запросу.

Важные нюансы при работе в форме объекта

Разберем детали, на которые следует обратить внимание при реализации логики в модуле формы:

  1. Проверка на новый объект. Если пользователь открыл форму нового, еще не записанного элемента справочника, свойство Объект.Ссылка будет содержать пустую ссылку. В этом случае срез регистра сведений вернет пустой результат, так как записей с пустой ссылкой в регистре нет. Перед выполнением запроса или метода ПолучитьПоследнее() следует проверять условие Объект.Ссылка.Пустая().
  2. Контекст выполнения. Вызовы запросов и обращение к менеджерам регистров сведений должны производиться на стороне сервера (директивы &НаСервере или &НаСервереБезКонтекста).
  3. Периодичность регистра. Если в конфигураторе для регистра установлена периодичность «По позиции регистратора», для точного позиционирования среза на момент документа рекомендуется использовать объект Граница с видом ВидГраницы.Включая.
← На главную